实在不知道怎么取一个标题去描述。
我有两个集合
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做缓存就行了。性能、存储,都不成问题。
看上去还是在按关系数据库的范式思路在设计表结构。为什么不把两个表合二为一?
一篇文章会具备的
field
是有限的,16MB的空间对于内容+字段来讲完全足够,所以最简单的方式:无论是删除或是查询都是一次完成。有什么特别的理由不能这样做吗?