MongoDB 4.4.12聚合查询IXSCAN高keysExamined问题排查求助
MongoDB 4.4.12聚合查询性能异常问题排查
场景描述
使用MongoDB 4.4.12版本,mycoll集合包含数百万级文档,myId字段设有唯一索引。执行以下聚合查询:
db.mycoll.aggregate([ { $match: { $and: [ { 'myId': { $in: [~ 1000 constants] } } ] } } ])
发现即使查询的queryHash与planCacheKey相同,且$in中的ID数量均约为1000,性能差异却极大:有的查询keysExamined为8000、耗时1000ms,有的keysExamined达350000、耗时50000ms,文档扫描数与数据读取量也相差悬殊,且该现象与是否重新规划查询无关。
更新:查询计划对比
带$and与不带$and的查询计划一致,关键片段如下:
... "optimizedPipeline" : true, "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "myId" : 1 }, "indexName" : "myId", "isMultiKey" : false, "multiKeyPaths" : { "myId" : [ ] }, "isUnique" : true, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "myId" : [ "[\"id1\", \"id1\"]", ..., "[\"idX\", \"idX\"]" ] } ...
问题解答
1. 仅查询1000个ID,为何唯一索引对应的keysExamined数值会如此之大?
- 索引范围扫描逻辑:MongoDB处理
$in条件时,会将每个ID转化为独立的索引范围(如["id1","id1"])。如果这些ID在索引中的分布极度分散,MongoDB需要逐个遍历每个范围对应的索引节点,过程中会扫描大量索引节点,导致keysExamined远大于目标ID数量。 - 不存在ID的索引查找:若
$in中的部分ID不存在于集合中,MongoDB仍会在索引中定位该ID的位置,这个查找过程会被计入keysExamined,即使最终没有匹配到文档。 - 索引碎片影响:数百万级文档的集合若经历过大量写入、删除或更新操作,索引容易产生碎片。碎片会导致MongoDB需要扫描更多的索引块才能定位到目标条目,直接拉高
keysExamined和查询耗时。
2. 多余的$and是否是性能差异的原因,换成不带$and的查询能否提升性能?
从查询计划的optimizedPipeline: true可以看出,MongoDB查询优化器已经自动将多余的$and语法优化掉了——带$and和不带$and的查询执行逻辑完全一致,不会产生性能差异。
因此换成如下不带$and的查询不会提升性能:
db.mycoll.aggregate([ { $match: { 'myId': { $in: [~ 1000 constants] } } } ])
性能差异的根源不在语法层面,建议从以下方向排查:
- 检查
myId索引的碎片率,必要时执行db.mycoll.reIndex()或重建索引 - 分析
$in中ID在索引中的分布情况,若分散度过高,可考虑分批查询或调整数据存储策略 - 监控查询时的内存缓存命中率,若部分查询的目标数据不在内存中,会导致磁盘IO耗时增加
内容的提问来源于stack exchange,提问作者Mark Barinstein
相关产品推荐
相关产品推荐

