浅谈MongoDB Count操作
Posted DBA入坑指南
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈MongoDB Count操作相关的知识,希望对你有一定的参考价值。
一、背景
今天笔者在同步线上一个库的时候直接使用count对集合进行大概的数据比对,发现两边的部分集合数据总数不匹配,源实例比目标实例多数据,为什么呢?让我们一步步去拨开迷雾吧!源实例和目标实例显示的某个集合count数据如下:
mongos> db.src_collection.count() # 源实例集合文档总数
84
mongos> db.dest_collection.count() # 目标实例集合文档总数
85
二、思路
怎么能快速地去排查问题,解决心中的疑惑呢?其实很简单,就拿上面那个小集合来说,我们可以查询两边集合的_id,然后大概比对一些数据量不就可以了? bingo,
源实例操作如下:
use grepwang_schema
DBQuery.shellBatchSize = 300 # 因为集合数据较少,所以将Mongo shell输出条目从默认的20调整为300
db.src_collection.find({},{_id:1})
将上面结果保存到old_mongo.txt
目标实例操作如下:
use grepwang_schema
DBQuery.shellBatchSize = 300
db.dest_collection.find({},{_id:1})
将上面结果保存到new_mongo.txt
我们使用如下命令进行比对:
[root@test grepwang]# wc -l old_mongo.txt
85 old_mongo.txt
[root@test grepwang]# wc -l new_mongo.txt
85 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}})
85
mongos> db.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操作的主要内容,如果未能解决你的问题,请参考以下文章