有两个集合
data 存储数据,
attach 存储数据的附件
data和attach,一对多关系。
data和attach的数量比大概1:10左右,也就是1条data数据,下面可能有10-50条数据。
data几千万的时候,attach可能就是几亿了。
现在的问题是,假如用户删除了data,那么attach也必须相应的删除。
对几亿条数据进行操作,这个过程是很漫长的。数据库压力也很大。
所以考虑是不是可以软删除,先把data更新状态为删除,attach先不管。然后后期用程序一点一点的在后台清除。
毕竟更新个几千万数据,等待时间还是不算长的。
但有个问题,attach是需要做数据统计的。比如用户删除之前,计算出他的附件占用空间20G,删除之后,你要给他删除之后的附件占用量,否则计费就不准确。
所以这个问题如何解决呢?
这是个很常见的问题,在大数据场景中很多时候确实是用软删除代替删除,所以方案本身没有问题。第一点是必须做的。如果你对GridFS有了解,其实在GridFS的
fs.files
中的记录就相当于你的data
,而fs.chunks
则相当于你的attach
。在fs.files
中就存有文件大小,文件名,路径等等一系列元数据。另外既然你已经标记了这个文件删除,那么在统计文件大小的时候可以很简单地根据删除标记过滤一下就好了。例如通过
isDeleted=true
标记了删除,则你需要的查询是:为了让这个查询更快,可以从以下几个方面做优化:
合理的索引
如果对数据库比较熟悉应该都能看出来这里最合适的索引是:
进一步的优化
实际上因为你关心的是“没有被删除的文件”,而不关心“已经被删除的文件”,所以进一步的优化可以考虑只对没有删除的文件做索引,即MongoDB中的部分索引
再进一步的优化
如果这个查询的性能非常重要且频繁,那么更进一步的优化可以考虑查询覆盖(Covered Query)。在其他数据库中应该也有类似的概念,即通过索引查询后直接从索引中获得数据,而不用查询数据页。这在大规模统计的时候往往会有帮助。其代价则是消耗更大的内存空间。或者说你必须有更大的内存才有可能从中获益。这时应该建立的索引和相应的查询应该是:
EDIT
关于删除的问题,删除确实是一个很重的操作,每条删除的数据都会导致全部相关的索引做变更,因此速度不会太快。你也已经提到了“软删除”,然后再由另外的线程来完成工作,这本质上是一种异步删除操作,思路没有问题,不过可以更简单一些,即在删除的时候同时设置一个过期时间,利用MongoDB的TTL Index帮助你自动完成删除操作。从节省空间的角度,这个过期时间的索引也只针对删除的数据有效,所以也是部分索引:
在操作删除时,只需要:
这样在删除时过期时间被设置为现在(马上过期),那么下一次TTL Index启动时(一分钟一次)就会帮助你删除这条数据。同样的操作也需要在attah上面完成。