Mongo低版本 count操作进行$in时走了覆盖索引却仍然回表

概要

由于历史原因,一些老项目还在用MongoDB V3.2版本,集群为分片模式,一个客户表数量有15亿左右,昨天监控突然报很多慢查询,如下:

db.info.count({domain_id:888,status:{$in:[2,3]}})

表结构关键字段如下:

字段类型注释
domain_idint64公司id
customer_idint64公司下的客户id
statusint32客户状态

索引如下:

db.info.createIndex({domain_id:1,status:1,modify_time:1})

一、原因

遇到慢查询,当然要explain下看下查询语句执行状况了,如下:

db.info.explain('executionStats').count({domain_id:4730422,status:{$in:[2,3]}})
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "customer.info",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "$and" : [
                                {
                                        "domain_id" : {
                                                "$eq" : 4730422
                                        }
                                },
                                {
                                        "status" : {
                                                "$in" : [
                                                        2,
                                                        3
                                                ]
                                        }
                                }
                        ]
                },
                "winningPlan" : {
                        "stage" : "COUNT",
                        "inputStage" : {
                                "stage" : "FETCH",
                                "inputStage" : {
                                        "stage" : "IXSCAN",
                                        "keyPattern" : {
                                                "domain_id" : 1,
                                                "status" : 1,
                                                "modify_time" : -1
                                        },
                                        "indexName" : "domain_id_1_status_1_modify_time_-1",
                                        "isMultiKey" : false,
                                        "direction" : "forward",
                                        "indexBounds" : {
                                                "domain_id" : [
                                                        "[4730422.0, 4730422.0]"
                                                ],
                                                "status" : [
                                                        "[2.0, 2.0]",
                                                        "[3.0, 3.0]"
                                                ],
                                                "modify_time" : [
                                                        "[MaxKey, MinKey]"
                                                ]
                                        }
                                }
                        }
                },
                "rejectedPlans" : []
        },
        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 0,
                "executionTimeMillis" : 0,
                "totalKeysExamined" : 9,   #索引扫描个数
                "totalDocsExamined" : 9,   #文档扫描个数,不等于0表示有回表
                "executionStages" : {
                        "stage" : "COUNT",
                        "nReturned" : 0,
                        "executionTimeMillisEstimate" : 0,
                        "works" : 10,
                        "advanced" : 0,
                        "needTime" : 9,
                        "needFetch" : 0,
                        "saveState" : 0,
                        "restoreState" : 0,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "nCounted" : 9,
                        "nSkipped" : 0,
                        "inputStage" : {
                                "stage" : "FETCH",
                                "nReturned" : 9,
                                "executionTimeMillisEstimate" : 0,
                                "works" : 10,
                                "advanced" : 9,
                                "needTime" : 0,
                                "needFetch" : 0,
                                "saveState" : 0,
                                "restoreState" : 0,
                                "isEOF" : 1,
                                "invalidates" : 0,
                                "docsExamined" : 9,
                                "alreadyHasObj" : 0,
                                "inputStage" : {
                                        "stage" : "IXSCAN",
                                        "nReturned" : 9,
                                        "executionTimeMillisEstimate" : 0,
                                        "works" : 10,
                                        "advanced" : 9,
                                        "needTime" : 0,
                                        "needFetch" : 0,
                                        "saveState" : 0,
                                        "restoreState" : 0,
                                        "isEOF" : 1,
                                        "invalidates" : 0,
                                        "keyPattern" : {
                                                "domain_id" : 1,
                                                "status" : 1,
                                                "modify_time" : -1
                                        },
                                        "indexName" : "domain_id_1_status_1_modify_time_-1",
                                        "isMultiKey" : false,
                                        "direction" : "forward",
                                        "indexBounds" : {
                                                "domain_id" : [
                                                        "[4730422.0, 4730422.0]"
                                                ],
                                                "status" : [
                                                        "[2.0, 2.0]",
                                                        "[3.0, 3.0]"
                                                ],
                                                "modify_time" : [
                                                        "[MaxKey, MinKey]"
                                                ]
                                        },
                                        "keysExamined" : 9,
                                        "dupsTested" : 0,
                                        "dupsDropped" : 0,
                                        "seenInvalidated" : 0,
                                        "matchTested" : 0
                                }
                        }
                } 
        },
        "ok" : 1
}

通过上面索引执行分析可以看到 totalDocsExamined不为0,所以进行了回表,理论上走了覆盖索引是不用回表的。
我们下面再试试没有$in的情况下,查询分析:

db.info.explain('executionStats').count({domain_id:4730422,status:3})
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "customer.info",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "$and" : [
                                {
                                        "domain_id" : {
                                                "$eq" : 4730422
                                        }
                                },
                                {
                                        "status" : {
                                                "$eq" : 3
                                        }
                                }
                        ]
                },
                "winningPlan" : {
                        "stage" : "COUNT",
                        "inputStage" : {
                                "stage" : "COUNT_SCAN",
                                "keyPattern" : {
                                        "domain_id" : 1,
                                        "status" : 1,
                                        "modify_time" : -1
                                },
                                "indexName" : "domain_id_1_status_1_modify_time_-1",
                                "isMultiKey" : false
                        }
                },
                "rejectedPlans" : [ ]
        },
        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 0,
                "executionTimeMillis" : 0,
                "totalKeysExamined" : 8,
                "totalDocsExamined" : 0,
                "executionStages" : {
                        "stage" : "COUNT",
                        "nReturned" : 0,
                        "executionTimeMillisEstimate" : 0,
                        "works" : 8,
                        "advanced" : 0,
                        "needTime" : 7,
                        "needFetch" : 0,
                        "saveState" : 0,
                        "restoreState" : 0,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "nCounted" : 6,
                        "nSkipped" : 0,
                        "inputStage" : {
                                "stage" : "COUNT_SCAN",
                                "nReturned" : 6,
                                "executionTimeMillisEstimate" : 0,
                                "works" : 7,
                                "advanced" : 6,
                                "needTime" : 1,
                                "needFetch" : 0,
                                "saveState" : 0,
                                "restoreState" : 0,
                                "isEOF" : 1,
                                "invalidates" : 0,
                                "keysExamined" : 8,
                                "keyPattern" : {
                                        "domain_id" : 1,
                                        "status" : 1,
                                        "modify_time" : -1
                                },
                                "indexName" : "domain_id_1_status_1_modify_time_-1",
                                "isMultiKey" : false
                        }
                },
                "allPlansExecution" : [ ]
        },
        "ok" : 1
}

可以看到此时totalDocsExamined的值为0,说明不使用$in操作就不会回表了。这显然是count操作在$in时的bug。
我们用docker快速搭建一个MongoDB V4.4的副本集试试,如下:

rsm:PRIMARY> db.info.explain('executionStats').count({domain_id:15600,status:{$in:[2,3]}})
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "customer.info",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "$and" : [
                                {
                                        "domain_id" : {
                                                "$eq" : 15600
                                        }
                                },
                                {
                                        "status" : {
                                                "$in" : [
                                                        2,
                                                        3
                                                ]
                                        }
                                }
                        ]
                },
                "winningPlan" : {
                        "stage" : "COUNT",
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "domain_id" : 1,
                                        "status" : 1
                                },
                                "indexName" : "domain_id_1_status_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "domain_id" : [ ],
                                        "status" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "domain_id" : [
                                                "[15600.0, 15600.0]"
                                        ],
                                        "status" : [
                                                "[2.0, 2.0]",
                                                "[3.0, 3.0]"
                                        ]
                                }
                        }
                },
                "rejectedPlans" : [ ]
        },
        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 0,
                "executionTimeMillis" : 0,
                "totalKeysExamined" : 5,
                "totalDocsExamined" : 0, 
                "executionStages" : {
                        "stage" : "COUNT",
                        "nReturned" : 0,
                        "executionTimeMillisEstimate" : 0,
                        "works" : 6,
                        "advanced" : 0,
                        "needTime" : 5,
                        "needYield" : 0,
                        "saveState" : 0,
                        "restoreState" : 0,
                        "isEOF" : 1,
                        "nCounted" : 5,
                        "nSkipped" : 0,
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "nReturned" : 5,
                                "executionTimeMillisEstimate" : 0,
                                "works" : 6,
                                "advanced" : 5,
                                "needTime" : 0,
                                "needYield" : 0,
                                "saveState" : 0,
                                "restoreState" : 0,
                                "isEOF" : 1,
                                "keyPattern" : {
                                        "domain_id" : 1,
                                        "status" : 1
                                },
                                "indexName" : "domain_id_1_status_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "domain_id" : [ ],
                                        "status" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "domain_id" : [
                                                "[15600.0, 15600.0]"
                                        ],
                                        "status" : [
                                                "[2.0, 2.0]",
                                                "[3.0, 3.0]"
                                        ]
                                },
                                "keysExamined" : 5,
                                "seeks" : 1,
                                "dupsTested" : 0,
                                "dupsDropped" : 0
                        }
                }
        },
        "serverInfo" : {
                "host" : "b8b178041a4e",
                "port" : 27021,
                "version" : "4.4.28",
                "gitVersion" : "61c2baf63a060f7c12bd76e779044800ae18710b"
        },
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1711857956, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1711857956, 1)
}

可以看到此时totalDocsExamined的值为0,说明至少MongoDB V4.4以后该bug修复了。

二、解决方法

1:升级MongoDB版本
2:既然不使用$in就可以,那就count两次,程序中累加起来。

由此可以看出,有条件的话,项目的基础组件及时升级也是很有必要的,新的版本肯定是修复了以往bug、做了性能优化以及功能新增的,好处多多。否则项目必然会慢慢腐化。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值