一个 mongodb数据库设计的难题

实在不知道怎么取一个标题去描述。

我有两个集合

data集合:存储文章信息,比如状态,分类,时间等等
field集合,存储文章的字段信息,因为每篇文章的字段不固定的,比如 title(标题)content(内容)等等

field 某一条数据如下

{
   field_key:'title',
   data_id:ObjectId("5c1beb6c559cd15f2d57cbc8"),
   data:'这是标题',
   data_hash:'a46c23269ab827c5f878e766984e4716'//文本的hash值
}

目前的问题是,我需要检测一个字段是否存在。

比如检测标题字段是否已经存在一样的,使用这么一个查询

{
    field_key:'title',
    data_hash:'a46c23269ab827c5f878e766984e4716',
}

看起来没问题,是吧,很简单的问题。但其实不然。

当数据量大的时候,比如data表几千万,那么field表可能达到上亿条数据。

假如用户删除了 uid=100 的文章数据,那么field表和data表都要删除。
当初为了减少用户的等待,我只是对data表进行了软删除,比如标记 status=-1表示这个数据已经被删除了。
毕竟对几亿条数据的field表进行删除和更新,是一个很耗性能耗时间的操作。所以这个删除机制是这样设计的。

所以问题就来了。上面检测结果,可能查询到多条field的情况。这时候你怎么知道这个field是不是对应的文章已经删除了?

所以每次你都要 把每一个查询到的field去data表里面查一下,他的status,这样对数据库压力是很大的。

我的解决方案:

第一个方案:我打算把每一个field的值,都用redis存储。比如 md5(field_key+data_hash)
放到一个set里面,几亿个,估计也占不了多少空间,这样查询性能肯定有很大改善。
但新的问题就来了,假如用户删除了其中的1000篇文章,我需要去数据库查询具体影响了多少条的field,然后再去set里面移除。这个删除性能可能有点影响。除此之外,倒还划算。

第二个方案:还是更改删除机制,把field集体更新status=-1,但我没测试过,假如几亿条数据的field集合,批量更新,比如更新100万条数据,所花费的时间是多少,会不会对数据库进行堵塞,影响业务。所以这个比较冒险。

不知道还有没有其他简单有效的方案?

--------补充------------

针对答案里面,为什么要分开两个数据表。之所以这样设计是有原因的:

1、是我没表达清楚。我说的是文章,只是简化了问题。(我要是洋洋洒洒写了几千上万字,估计你们都懒得看问题了)

实际上,即可以是文章,也可以是商品数据,也可以是微博数据,我们需要存储的数据模型是多样化的。

比如我存储一篇微博的数据,可能这条微博有几十万评论,除了评论,还有评论时间,评论用户信息等等。
只要有16MB的限制,我这个系统就有局限,所以不能这样设计。

2、一篇文章的内容还有多个版本,比如版本1,版本2,版本3。至于有多少个版本,无法给出答案。所以16MB肯定不够用的。

还有其他各种原因,就不详细解释了。但是可以肯定的是,必须分开2个数据表。

我最终使用redis存储了。看起来这是比较好的方案,除此之外,我想不出其他更好的方案了。

3、至于范式不范式的问题。

可能有人就会问了,你这样存储的话,查询一篇文章的数据,岂不是很不方便?

我们另外还有一份数据存储到了elasticsearch里面。elasticsearch最大的特点,数据冗余,不遵循范式。

就像楼下所描述的那样,我们在elasticsearch里面是这样存储的:

// data表
{
    // data表字段
    fields: [
        {
           field_key:'title',
           data_id:ObjectId("5c1beb6c559cd15f2d57cbc8"),
           data:'这是标题',
        },
        {
           field_key:'content',
           data_id:ObjectId("5c1beb6c559cd15f2d57cbc8"),
           data:'这是内容',
        },
        ...
    ]
}

只需要一次查询,所有的东西都出来了,这个不成问题。所以我们在mongodb里面尽可能遵循范式。


使用redis存储,观察了一段时间,目前所有的set里面的东西,存放了1000万个左右。存储空间大概1G左右。
也就是一个亿的话,估计10G,势必造成性瓶颈。这也不是一个好办法。

-----------------补充-------------------

我突然发现自己好蠢,妈的。redis的set集合,使用mongodb存储不就好了吗?
用redis做缓存就行了。性能、存储,都不成问题。

阅读 1.7k
2 个回答

看上去还是在按关系数据库的范式思路在设计表结构。为什么不把两个表合二为一?
一篇文章会具备的field是有限的,16MB的空间对于内容+字段来讲完全足够,所以最简单的方式:

// data表
{
    // data表字段
    fields: [
        {
           field_key:'title',
           data_id:ObjectId("5c1beb6c559cd15f2d57cbc8"),
           data:'这是标题',
           data_hash:'a46c23269ab827c5f878e766984e4716'//文本的hash值
        },
        {
           field_key:'title',
           data_id:ObjectId("5c1beb6c559cd15f2d57cbc8"),
           data:'这是标题',
           data_hash:'a46c23269ab827c5f878e766984e4716'//文本的hash值
        },
        ...
    ]
}

无论是删除或是查询都是一次完成。有什么特别的理由不能这样做吗?

同上。我也很好奇为什么要分成两个 collection 存储。MongoDB 很擅长处理这种标准文档型数据的。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏