Mongodb 为什么覆盖计数查询很慢?
我正在运行mongodb v3.2.12分片集群。切分键是Mongodb 为什么覆盖计数查询很慢?,mongodb,mongodb-query,Mongodb,Mongodb Query,我正在运行mongodb v3.2.12分片集群。切分键是\u id,它是md5散列。 问题是,覆盖计数查询需要花费很多时间 每个mongodb节点上使用的索引大约为5GB。所有索引的总大小为32GB,完全适合RAM,因为每个节点都有128GB的RAM 查询是:db.offer.count({“shopId”:275419,“missingSince”:null}) 使用的索引已创建为:db.offer.createIndex({shopId:1,missingFrom:1,merchantId
\u id
,它是md5散列。
问题是,覆盖计数查询需要花费很多时间
每个mongodb节点上使用的索引大约为5GB。所有索引的总大小为32GB,完全适合RAM,因为每个节点都有128GB的RAM
查询是:db.offer.count({“shopId”:275419,“missingSince”:null})
使用的索引已创建为:db.offer.createIndex({shopId:1,missingFrom:1,merchantId:1,{u id:1},{background:true})
如您所见,索引不是稀疏的,因此索引中甚至存在空值
在查询运行时运行db.currentOp()
表明查询使用了正确的索引,但是,它已经运行了2814秒以上:
{
"desc" : "conn56062",
"threadId" : "140131556767488",
"connectionId" : 56062,
"client_s" : "x.x.x.x:39177",
"active" : true,
"opid" : "offerStoreIT02:1075309911",
"secs_running" : 2814,
"microsecs_running" : NumberLong("2814791918"),
"op" : "command",
"ns" : "offerStore.offer",
"query" : {
"query" : {
"count" : "offer",
"query" : {
"missingSince" : null,
"shopId" : 275419
}
},
"$readPreference" : {
"mode" : "primaryPreferred"
}
},
"planSummary" : "IXSCAN { shopId: 1.0, missingSince: 1.0, merchantId: 1.0, _id: 1.0 }",
"numYields" : 249244,
"locks" : {
"Global" : "r",
"Database" : "r",
"Collection" : "r"
},
"waitingForLock" : false,
"lockStats" : {
"Global" : {
"acquireCount" : {
"r" : NumberLong(498490)
}
},
"Database" : {
"acquireCount" : {
"r" : NumberLong(249245)
}
},
"Collection" : {
"acquireCount" : {
"r" : NumberLong(249245)
}
}
}
}
在内存中迭代5GB的索引从来不会花费这么多时间。在查询运行时,每个mongodb主服务器都在以75-100 MB/秒的速度不断地从磁盘读取数据。当查询未运行时,从磁盘读取的速度只有5-10 MB/秒,因此我的假设是mongodb将文档从SSD提取到内存中,以便对它们进行计数
但为什么会这样呢?索引应涵盖查询,因为索引中存在所有字段,包括shardkey,根据mongodb文档,这些字段应足以涵盖查询:
跟进:
我把这个问题分解成一个简单的、不分块的设置。我插入了以下类型的文件:
- a) 3个文档,没有两个字段
和shopId
missingSing
- b) 5个带有字段
shopId的文档:1个
没有字段
丢失,因为
- c) 7个带有字段
和shopId:1
missingSince:null
- d) 13个带有字段
和shopId:1
丢失的文档自:ISODate(“2017-05-22T07:52:40.831Z”)
{shopId:1,missingSince:1}
。
查询计数({“shopId”:1,“missingSince”:null})的执行计划指示“totalDocsExamined”:12
,这意味着必须获取12个文档。这些文件必须是b)的5份文件加上c)的7份文件。所有这12个文档都应该位于索引中,索引中的shopId为1,missingSince:null
,因此满足查询要求
但是为什么mongodb仍然需要获取和检查这12个文档?
这是我的测试集:
rs1:PRIMARY> db.offer.find()
{ "_id" : 1, "v" : 1 }
{ "_id" : 2, "v" : 1 }
{ "_id" : 3, "v" : 1 }
{ "_id" : 4, "shopId" : 1, "v" : 1 }
{ "_id" : 5, "shopId" : 1, "v" : 1 }
{ "_id" : 6, "shopId" : 1, "v" : 1 }
{ "_id" : 7, "shopId" : 1, "v" : 1 }
{ "_id" : 8, "shopId" : 1, "v" : 1 }
{ "_id" : 9, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 10, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 11, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 12, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 13, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 14, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 15, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 16, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 17, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 18, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 19, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 20, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 21, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 22, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 23, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 24, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 25, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 26, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 27, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 28, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
db.offer.createIndex({missingSince:1,shopId:1}, {background:true})
以下是explain()的输出:
请尝试以下索引:
如果您现有的索引db.offer.createIndex({shopId:1,missingFrient:1,merchantId:1,{background:true})
用于另一个目的,请尝试在提供的集合中创建以下索引:
rs1:PRIMARY> db.offer.find()
{ "_id" : 1, "v" : 1 }
{ "_id" : 2, "v" : 1 }
{ "_id" : 3, "v" : 1 }
{ "_id" : 4, "shopId" : 1, "v" : 1 }
{ "_id" : 5, "shopId" : 1, "v" : 1 }
{ "_id" : 6, "shopId" : 1, "v" : 1 }
{ "_id" : 7, "shopId" : 1, "v" : 1 }
{ "_id" : 8, "shopId" : 1, "v" : 1 }
{ "_id" : 9, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 10, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 11, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 12, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 13, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 14, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 15, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 16, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 17, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 18, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 19, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 20, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 21, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 22, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 23, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 24, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 25, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 26, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 27, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 28, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
db.offer.createIndex({missingSince:1,shopId:1}, {background:true})
这将优化查询本身,从而计算结果。由于没有人能找到导致此问题的有效原因,我昨天打开了一份mongodb错误报告:
Mongodb工程师确认这是一个bug。不幸的是,mongodb的文档中没有提到这一点,这将节省我们很多时间追踪问题,并从一开始就部署另一个架构设计。offer collection中有多少文档?与包含计数时相比,不包含计数的查询是否运行得更快?为什么此索引中有
\u id
字段?这基本上是多余的,因为\u id
字段上已经有了索引。我认为只要删除这个索引,使用一个没有包含\u id
字段的索引,您就会得到很大的提升。出于测试目的,只为您想要作为标准的字段创建一个索引,并使用.hint()
强制选择该索引.FYI。“覆盖查询”的一般概念是,所需的所有数据都包含在所选索引中。10次中有9次,这里的大杀手希望检索所用索引字段中包含的更多数据。但是如果您只是执行.count()
,那么这基本上否定了文档检索的效果。由于实际上没有检索到任何文档。@Astro,因此该集合中有2.5亿个文档。“查找”与“计数”一样慢。您可以尝试下面提到的索引吗?此索引的效果会差得多,因为第一个字段“missingSince”的选择性不如第二个字段“shopId”。使用这样的索引查询missingSince:null
,将导致几乎完全的集合扫描。