1. 慢查与表详情

1.1 慢查日志

1)查询中命中了表 orderPrinter.order_153551 的索引 

IXSCAN { deptCode: 1, createTime: 1, isCPrinted: 1 }
mongodb日志分析:{
    "replanReason": "cached plan was less efficient than expected: expected trial execution to take 57 works but it took at least 570 works",
    "op": "query",
    "ns": "orderPrinter.order_153551",
    "planSummary": "IXSCAN { deptCode: 1, createTime: 1, isCPrinted: 1 }",
    "nreturned": 0,
    "responseLength": 239,
    "storage": {
        
    },
    "locks": {
        "FeatureCompatibilityVersion": {
            "acquireCount": {
                "r": {
                    "$numberLong": "19"
                }
            }
        },
        "Database": {
            "acquireCount": {
                "r": {
                    "$numberLong": "18"
                }
            }
        },
        "Collection": {
            "acquireCount": {
                "r": {
                    "$numberLong": "18"
                }
            }
        },
        "Mutex": {
            "acquireCount": {
                "r": {
                    "$numberLong": "1"
                }
            }
        },
        "ReplicationStateTransition": {
            "acquireCount": {
                "w": {
                    "$numberLong": "19"
                }
            }
        },
        "Global": {
            "acquireCount": {
                "r": {
                    "$numberLong": "19"
                }
            }
        }
    },
    "flowControl": {
        
    },
    "command": {
        "filter": {
            "isCPrinted": 0,
            "createTime": {
                "$gte": {
                    "$date": "2025-07-22T08:20:00.195+08:00"
                }
            },
            "stallNumber": null,
            "dealDeliveryType": {
                "$ne": -1
            },
            "delStatus": null,
            "requiredDeliveryTime": {
                "$lte": {
                    "$date": "2025-08-05T23:59:59.999+08:00"
                }
            },
            "deptCode": "10019"
        },
        "lsid": {
            "id": {
                "$binary": "oVA3fU/pTmqOO1mkuIvseg==",
                "$type": "04"
            }
        },
        "$db": "orderPrinter",
        "$clusterTime": {
            "clusterTime": {
                "$timestamp": {
                    "t": 1754353200,
                    "i": 37
                }
            },
            "signature": {
                "keyId": {
                    "$numberLong": "7485993259508957185"
                },
                "hash": {
                    "$binary": "DQXPPvYqOy7Bg5X3ZXCQt4chDJU=",
                    "$type": "00"
                }
            }
        },
        "find": "order_153551",
        "limit": 10,
        "sort": {
            "createTime": -1
        }
    },
    "replanned": true,
    "queryHash": "FE251C8C",
    "protocol": "op_msg",
    "keysExamined": 4001,
    "planCacheKey": "79D2F979",
    "numYield": 17,
    "replRole": {
        "stateStr": "PRIMARY",
        "_id": 2
    },
    "docsExamined": 0,
    "cursorExhausted": true,
    "millis": 151,
    "fromMultiPlanner": true
}

1.2 表数据量与索引

mgset-67375689:PRIMARY> db.order_153551.count()
356052
mgset-67375689:PRIMARY> db.order_153551.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_"
        },
        {
                "v" : 2,
                "unique" : true,
                "key" : {
                        "orderNum" : 1
                },
                "name" : "orderNum_1"
        },
        {
                "v" : 2,
                "key" : {
                        "deptCode" : 1
                },
                "name" : "deptCode_1"
        },
        {
                "v" : 2,
                "key" : {
                        "deptCode" : 1,
                        "createTime" : 1,
                        "isCPrinted" : 1
                },
                "name" : "deptCode_1_createTime_1_isCPrinted_1"
        },
        {
                "v" : 2,
                "key" : {
                        "deptCode" : 1,
                        "createTime" : 1,
                        "requiredDeliveryTime" : 1,
                        "isCPrinted" : 1,
                        "dealDeliveryType" : 1
                },
                "name" : "idx_deptCode_createTime_requiredDeliveryTime",
                "background" : true
        },
        {
                "v" : 2,
                "key" : {
                        "createTime" : 1
                },
                "name" : "createTime_1",
                "background" : true
        },
        {
                "v" : 2,
                "key" : {
                        "isCPrinted" : 1,
                        "createTime" : -1,
                        "deptCode" : 1
                },
                "name" : "isCPrinted_1_createTime_-1_deptCode_1",
                "background" : true
        }
]
mgset-67375689:PRIMARY> 

2. 慢SQL查询计划分析

这条 MongoDB 日志记录了一次查询执行计划重规划(replan) 的过程,核心原因是 “缓存的执行计划效率远低于预期”。以下是详细分析及优化建议:

2.1 日志核心信息拆解

关键字段含义解读
replanReason 重规划原因:缓存的计划预期只需 57 个 “工作单元”(works),但实际至少需要 570 个,效率差距 10 倍以上。
op 操作类型:query(查询操作)。
ns 操作的集合:orderPrinter.order_153551
planSummary 执行计划摘要:使用索引扫描(IXSCAN),索引为 { deptCode: 1, createTime: 1, isCPrinted: 1 }
command.filter 查询过滤条件:
isCPrinted: 0
createTime: {$gte: 2025-07-22 08:20:00}
dealDeliveryType: {$ne: -1}
delStatus: null
requiredDeliveryTime: {$lte: 2025-08-05 23:59:59}
deptCode: "10019"
command.sort 排序条件:{ createTime: -1 }(按创建时间倒序)。
keysExamined 扫描的索引键数量:4001(通过索引扫描了 4001 个键)。
docsExamined 扫描的文档数量:0(未扫描实际文档,说明索引覆盖了查询需求)。
millis 执行耗时:151 毫秒(不算高效,因重规划 + 扫描量较大)。
numYield 锁让步次数:17(查询过程中 17 次让出锁资源,可能因其他操作竞争或数据量过大)。

2.2 核心问题分析

1. 为什么会发生 “执行计划重规划”?

MongoDB 会缓存查询的执行计划以提升效率,但当实际执行效率与缓存计划的预期效率差距过大(如本例中实际工作量是预期的 10 倍)时,会触发重规划,重新计算更优的执行计划。

2. 可能的底层原因

  • 数据分布变化:集合中符合条件的文档数量(如deptCode: "10019"isCPrinted: 0)近期大幅增加,导致原缓存计划的估算(57 works)与实际(570 works)偏差过大。
  • 索引选择性下降:索引{ deptCode: 1, createTime: 1, isCPrinted: 1 }对当前查询的过滤性变弱(例如deptCode: "10019"的文档占比过高),导致需要扫描大量索引键(4001 个)才能返回结果。
  • 查询条件范围过宽:createTime的时间范围(从 2025-07-22 到 2025-08-05)过长,包含的文档过多,导致索引扫描效率下降。

2.3 优化建议

1. 优化索引结构

当前索引{ deptCode: 1, createTime: 1, isCPrinted: 1 }虽然包含了查询条件的字段,但可能存在字段顺序不合理的问题。建议按 “过滤性从强到弱” 调整字段顺序:

  • 优先放置过滤性强的字段(即该字段的条件能排除大部分文档)。例如:
    isCPrinted: 0的文档占比远低于deptCode: "10019",可调整为{ isCPrinted: 1, deptCode: 1, createTime: 1 }
  • 验证索引有效性:通过explain()分析查询计划,确认索引是否被高效使用(避免ALL全表扫描或IXSCAN扫描过多键)。

2. 缩小查询范围

  • 限制createTime的时间窗口:若业务允许,将查询的时间范围从 “近 15 天” 缩小(如 “近 7 天”),减少需要扫描的索引键数量。
  • 补充更具体的过滤条件:若delStatus: nulldealDeliveryType: {$ne: -1}的过滤性较强,可确保这些条件在查询中有效发挥作用(避免因字段无索引导致的效率问题)。

3. 处理计划缓存失效

  • 若频繁发生重规划,可手动清理缓存的执行计划(适用于临时优化):
    db.getCollection("order_153551").getPlanCache().clearPlansByQuery({
      filter: { 
        isCPrinted: 0,
        createTime: { $gte: ISODate("2025-07-22T08:20:00.195+08:00") },
        stallNumber: null,
        dealDeliveryType: { $ne: -1 },
        delStatus: null,
        requiredDeliveryTime: { $lte: ISODate("2025-08-05T23:59:59.999+08:00") },
        deptCode: "10019"
      },
      sort: { createTime: -1 },
      limit: 10
    });
    
     
  • 长期需监控数据分布变化,定期优化索引(如数据量增长后拆分集合或增加分片键)。

4. 减少锁竞争

numYield: 17说明查询过程中多次让锁,可能因集合并发写入高导致。可:

  • 避免在查询高峰期执行大量写入操作;
  • 若集合过大,考虑按deptCodecreateTime分片,分散锁竞争压力。

2.4 总结

本次日志的核心是 “缓存计划效率不足导致重规划”,主要与数据分布变化和索引效率下降相关。通过优化索引结构、缩小查询范围、清理无效计划缓存等措施,可减少重规划频率,提升查询效率。若问题频繁出现,需进一步分析集合的增长趋势和查询模式,制定长期的索引和存储优化策略。
 

3. 计算表的字符数据唯一性

3.1 两种计算方法

1)javascrite方式

算出字段唯一值,除以总数据量获得百分比

var isCPrinted=db.order_153551.distinct("isCPrinted")
var uniqueCount =isCPrinted.length
print("字段唯一值数量:", uniqueCount);

image

2)聚合方式

算出字段唯一值,除以总数据量获得百分比

db.order_153551.aggregate([
  { $group: { _id: "$deptCode" } },
  { $count: "deptCode 唯一值数量" }
])

image

4. 计算个字段数据量

4.1 计算

image

4.2 分析计算结果

从计算情况分析可知,此慢查数据分布不均衡,因此索引字段顺序可以按照 "isCPrinted":1,"createTime":-1,"deptCode":1 来创建

db.order_153551.createIndex({"isCPrinted":1,"createTime":-1,"deptCode":1},{"background" : true})

image

5. 新执行计划分析

5.1 新执行计划分析

1)执行计划走了新建的索引:

isCPrinted_1_createTime_-1_deptCode_1

2)"executionStats" 详细列出了新执行计划

        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 0,
                "executionTimeMillis" : 3,
                "totalKeysExamined" : 228,
                "totalDocsExamined" : 0,
                "executionStages" : {
                        "stage" : "FETCH",
                        "filter" : {
                                "$and" : [
                                        {
                                                "dealDeliveryType" : {
                                                        "$not" : {
                                                                "$eq" : -1
                                                        }
                                                }
                                        },
                                        {
                                                "delStatus" : {
                                                        "$eq" : null
                                                }
                                        },
                                        {
                                                "requiredDeliveryTime" : {
                                                        "$lte" : ISODate("2025-08-05T15:59:59.999Z")
                                                }
                                        },
                                        {
                                                "stallNumber" : {
                                                        "$eq" : null
                                                }
                                        }
                                ]
                        },
                        "nReturned" : 0,
                        "executionTimeMillisEstimate" : 0,
                        "works" : 229,
                        "advanced" : 0,
                        "needTime" : 227,
                        "needYield" : 0,
                        "saveState" : 1,
                        "restoreState" : 1,
                        "isEOF" : 1,
                        "docsExamined" : 0,
                        "alreadyHasObj" : 0,
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "nReturned" : 0,
                                "executionTimeMillisEstimate" : 0,
                                "works" : 228,
                                "advanced" : 0,
                                "needTime" : 227,
                                "needYield" : 0,
                                "saveState" : 1,
                                "restoreState" : 1,
                                "isEOF" : 1,
                                "keyPattern" : {
                                        "isCPrinted" : 1,
                                        "createTime" : -1,
                                        "deptCode" : 1
                                },
                                "indexName" : "isCPrinted_1_createTime_-1_deptCode_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "isCPrinted" : [ ],
                                        "createTime" : [ ],
                                        "deptCode" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "isCPrinted" : [
                                                "[0.0, 0.0]"
                                        ],
                                        "createTime" : [
                                                "[new Date(9223372036854775807), new Date(1753143600195)]"
                                        ],
                                        "deptCode" : [
                                                "[\"10019\", \"10019\"]"
                                        ]
                                },
                                "keysExamined" : 228,
                                "seeks" : 228,
                                "dupsTested" : 0,
                                "dupsDropped" : 0

5.2 新旧执行计划对比

对比新、老执行计划,核心差异体现在索引选择、扫描效率、执行时间三个关键维度,新计划整体性能更优,具体差异点如下:

1)最优索引选择(winningPlan

维度老执行计划新执行计划差异分析
使用的索引 deptCode_1_createTime_1_isCPrinted_1 isCPrinted_1_createTime_-1_deptCode_1 新计划选择了字段顺序和排序方向更匹配查询的索引:
- 老索引前导字段是deptCode,新索引前导字段是isCPrinted
- 新索引中createTime是降序(-1),与查询的sort({createTime: -1})完全匹配,无需反向扫描。
索引字段顺序 deptCode → createTime(升序) → isCPrinted isCPrinted → createTime(降序) → deptCode 新索引将过滤性更强的isCPrinted(枚举值,易过滤)放在前导位置,优先缩小范围;且createTime排序方向与查询一致,减少索引扫描成本。

2)执行效率核心指标(executionStats

指标老执行计划新执行计划差异分析
索引扫描量(totalKeysExamined 4015 228 新计划扫描的索引键数量仅为老计划的 5.6%,说明新索引的过滤性显著提升(更快锁定符合条件的范围)。
执行时间(executionTimeMillis 39 毫秒 3 毫秒 新计划执行时间缩短 92%,效率大幅提升,主要得益于索引扫描量的减少。
索引跳转次数(seeks 4015 228 新计划索引跳转次数减少,说明符合条件的索引键在物理存储上更连续,扫描更高效。
文档扫描量(totalDocsExamined 0 0 两者均实现 “索引覆盖”(无需回表读文档),此维度无差异。

3)查询过滤逻辑(filter与索引匹配度)

维度老执行计划新执行计划差异分析
索引覆盖的过滤条件 仅覆盖deptCodecreateTimeisCPrinted 覆盖isCPrintedcreateTimedeptCode 两者均覆盖核心过滤条件,但新索引因前导字段isCPrinted过滤性更强(假设isCPrinted:0的文档占比更低),优先过滤后,后续deptCodecreateTime的扫描范围更小。
额外过滤逻辑(filter阶段) 无额外过滤(索引直接覆盖) 需过滤dealDeliveryTypedelStatus等字段 新计划虽有少量额外过滤,但因索引扫描量已大幅减少,整体成本仍远低于老计划。

4)排序处理(sort与索引的匹配度)

维度老执行计划新执行计划差异分析
排序依赖 依赖索引反向扫描(direction: "backward" 直接利用索引顺序(direction: "forward" 老索引中createTime是升序,需反向扫描才能匹配sort: {createTime: -1};新索引createTime是降序,与排序完全一致,无需额外操作,节省扫描成本。

5)新计划更优的核心原因

  1. 索引字段顺序更合理:将过滤性更强的isCPrinted作为前导字段,优先缩小查询范围;
  2. 排序方向与索引匹配:createTime: -1的索引顺序直接支持排序,避免反向扫描;
  3. 扫描量大幅减少:从 4015 条索引键降至 228 条,直接降低执行时间。
建议保留新计划使用的索引isCPrinted_1_createTime_-1_deptCode_1,并可考虑删除老索引(如deptCode_1_createTime_1_isCPrinted_1)以减少维护成本。

6. 强制走索引hint 提示

6.1 语法

// 方式1:通过索引名称指定
db.集合名.find(查询条件).sort(排序条件).hint("索引名称");

// 方式2:通过索引键模式指定(与创建索引时的键模式一致)
db.集合名.find(查询条件).sort(排序条件).hint({ 索引键模式 });

6.2 例句

db.order_153551.explain("executionStats").find({
    isCPrinted: 0,
    createTime: {
        $gte: ISODate("2025-07-22T08:20:00.195+08:00")
    },
    stallNumber: null,
    dealDeliveryType: {
        $ne: -1
    },
    delStatus: null,
    requiredDeliveryTime: {
        $lte: ISODate("2025-08-05T23:59:59.999+08:00")
    },
    deptCode: "10019"
}).sort({
    createTime: -1
}).hint("idx_deptCode_createTime_requiredDeliveryTime");
 posted on 2025-08-05 10:49  xibuhaohao  阅读(29)  评论(0)    收藏  举报