浅谈MongoDB Count操作

Posted DBA入坑指南

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈MongoDB Count操作相关的知识,希望对你有一定的参考价值。

一、背景

        今天笔者在同步线上一个库的时候直接使用count对集合进行大概的数据比对,发现两边的部分集合数据总数不匹配,源实例比目标实例多数据,为什么呢?让我们一步步去拨开迷雾吧!源实例和目标实例显示的某个集合count数据如下:

mongosdb.src_collection.count()    # 源实例集合文档总数84mongosdb.dest_collection.count()   # 目标实例集合文档总数85


二、思路

        怎么能快速地去排查问题,解决心中的疑惑呢?其实很简单,就拿上面那个小集合来说,我们可以查询两边集合的_id,然后大概比对一些数据量不就可以了? bingo,

源实例操作如下:

mongos> use grepwang_schemamongos> DBQuery.shellBatchSize = 300    # 因为集合数据较少,所以将Mongo shell输出条目从默认的20调整为300mongos> db.src_collection.find({},{_id:1}) #将上面结果保存到old_mongo.txt

目标实例操作如下:

mongos> use grepwang_schemamongos> DBQuery.shellBatchSize = 300mongos> db.dest_collection.find({},{_id:1})     #将上面结果保存到new_mongo.txt

我们使用如下命令进行比对:

[root@test grepwang]# wc -l old_mongo.txt85 old_mongo.txt[root@test grepwang]wc -l new_mongo.txt85 new_mongo.txt[root@test grepwang]# grep -v -f old_mongo.txt new_mongo.txt[root@test grepwang]grep -v -f new_mongo.txt old_mongo.txt

从上面结果我们可以得出,两边的集合文档条目是一样的,我们不难想出,那问题应该是出现在count这个操作上了,有可能MongoDB count操作是一个预估值或者由于某些原因导致集合元数据信息不准确了。


三、处理流程

        为了排查我们的猜想,我们直接扫描整个集合进行count,以排除集合元数据信息不准确的问题,源实例和目标实例操作如下:

mongos> db.src_collection.count({_id:{$exists:true}})85mongosdb.dest_collection.count({_id:{$exists:true}})85

 从上面可以大概判断出集合元数据信息不准确导致的,可是为什么会导致信息不准确呢?其实这个跟通过查看mysql表状态看rows是类似的,因为MySQL的表状态信息并不是实时去更新,所以一般对于MySQL DBA来说,一般直接表执行count操作来得到表的行数。但MongoDB对于这点其实跟MySQL有点不一样,MongoDB的集合元数据信息是比较及时地进行变更的,有CUD都会导致计数器不断加1,所以很多MongoDB数据比对工具都是通过查看dbstat和collstat来进行的,当然正常情况下,这是没有多大问题,可以一旦出现下面这两种情况,count操作得到的结果有可能不匹配,得到的是一个预估值。

1、On a sharded cluster, the resulting count will not correctly filter out orphaned documents.

2、After an unclean shutdown, the count may be incorrect

对于第一种情况是很容易理解的,在分片集群下,由于balancer一般都是一致运行的,chunk很容易由于出现move导致count不准,所以在分片集群下,可以通过如下操作进行数据的count:

db.collection.aggregate( [ { $count: "myCount" }])或者db.collection.aggregate( [ { $group: { _id: null, count: { $sum: 1 } } } { $project: { _id: 0 } }] )

很明显,笔者比较大的可能出现了第二种情况,由于最近半个月这个副本集在凌晨有大量getmore导致了oom,所以前段时间对内存进行了一个升级,但是值得一提的是,这个问题依然是有一定概率发生的。


四、解决方法

        1、全集合扫描,这样子不会使用到集合元数据信息进行结果的返回了,操作如下:

mongos> db.collection.count({_id:{$exists:true}})

        2、重置计数器,操作如下:

db.collection.validate({ full: true })

注意:第二种方法存在比较的风险,会影响业务,首先validate操作会消耗比较多资源,可能会影响MongoDB实例的性能,其次validate会对所操作的集合加x锁,在操作完成之前会阻塞所有的读写,这在大集合下是致命的,如果在seconary运行,可能会阻塞seconary所有的其他操作,直至操作完成,这个代价是比较大的,当然如果业务没有使用到seconary也可以使用,这个就看实际情况而定吧!


五、建议

        1、优先使用第一种方法,如果怕对业务产生影响,可以在业务顶峰期进行集合count,也可以在seconary进行操作。

        2、迁移MongoDB数据时,不建议使用dbstat和collstat进行集合总数的count,现在市面上大部分的MongoDB数据校验工具都是基于这个来实现的,会导致误差,建议自己基于以上方法进行集合总数的校验,另外数据校验还要考虑索引的比对和数据的比对,从而编写数据校验工具来确保前后迁移数据的一致。


喜欢作者,可以关注一下


以上是关于浅谈MongoDB Count操作的主要内容,如果未能解决你的问题,请参考以下文章

[MongoDB]count,gourp,distinct

如何将代码片段存储在 mongodb 中?

浅谈mongodb的使用场景及优缺点

MongoDB——聚合管道之$match和$count操作

mongodb聚合(转)

浅谈时间复杂度