MongoDB 7.0 无独立“查询重写机制”,所谓“重写”实为查询优化器对等价查询结构的自动归一化处理,仅影响内部 queryShapeHash 计算与索引选择,不改变用户语句,也不提供 queryRewrite 相关命令或配置接口。

MongoDB 7.0 没有“查询重写机制”这一独立功能,所谓“重写”是查询优化器对等价查询结构的自动归一化处理,它不改变用户输入的 find() 或 aggregate() 语句,只影响内部 queryShapeHash 计算和索引选择逻辑。
为什么你搜不到 queryRewrite 或 rewriteRules 相关命令
MongoDB 官方文档、explain() 输出、system.profile 字段中均不存在 queryRewrite、rewriteRules 或类似配置项。所谓“新重写机制”实为 6.0+ 中 queryShape 标准化能力的增强——它把不同写法但语义一致的查询(如 {a: {$eq: 1}} 和 {a: 1})映射到同一个 queryShapeHash,便于慢日志聚合与索引推荐。
- 这不是可开关的功能,也不暴露给用户控制;它是引擎底层行为,仅在
explain("queryPlanner")的queryPlanner.queryHash和queryPlanner.queryShapeHash字段中体现 - 你无法用
db.runCommand({ setParameter: 1, ... })启用/禁用它,也不存在db.adminCommand({ getRewriteStats: 1 })这类接口 - 如果你在第三方工具或博客里看到“启用查询重写”,基本是混淆了 MongoDB 与 PostgreSQL(有
pg_rewrite)或 Elasticsearch(有query_rules)的概念
如何验证 queryShape 是否真正影响索引选择
关键不是看“有没有重写”,而是看同一 queryShapeHash 下不同写法的查询是否命中相同索引、产生相似执行计划:
- 先执行两个语义等价但写法不同的查询,比如:
db.orders.find({ status: "paid" })和db.orders.find({ status: { $eq: "paid" } }) - 分别运行
explain("queryPlanner"),对比输出中的queryPlanner.queryShapeHash—— 它们应该一致 - 再比对
queryPlanner.winningPlan.inputStage.indexName和executionStats.totalKeysExamined:若两者都走同一个索引且扫描键数接近,说明 queryShape 归一化生效;若一个走COLLSCAN、另一个走IXSCAN,那问题出在索引字段类型不匹配(比如status被建成了text索引)或查询条件隐式类型转换 - 注意:
$or、$in、正则表达式等操作会破坏 queryShape 等价性,即使值相同,{a: {$in: [1]}}和{a: 1}的queryShapeHash也不同
索引利用率要看 $indexStats + executionStats.totalKeysExamined
别被“用了索引”骗了。真正反映利用率的是索引被读取的频次与效率,而非是否出现在 winningPlan 里:
- 查真实访问次数:
db.orders.aggregate([ { $indexStats: {} }, { $sort: { "accesses.ops": -1 } } ])——accesses.ops是自 3.2 起累计的命中数,为 0 的索引大概率可删 - 查单次效率:对慢查询跑
explain("executionStats"),重点看executionStats.totalKeysExamined / executionStats.nReturned比值。若 > 10,说明索引过滤能力弱(比如前缀字段低基数),或查询未对齐索引顺序 - 复合索引陷阱:建了
{a: 1, b: 1, c: 1},但查询只用{b: 1}或{c: 1},totalKeysExamined会飙升,因为 B-tree 无法跳过前缀直接定位中间字段 - 7.0 中
executionStats.executionStages.stage出现"IXSCAN"不代表高效,还要看inputStage.keyPattern是否是你预期的索引,以及是否有"stage": "FETCH"—— 有就说明没覆盖,还得回表读文档
真正容易被忽略的点是:queryShapeHash 一致 ≠ 性能一致。当查询含时间戳、ObjectId 或随机值时,query 字段本身不可聚合,必须依赖 queryShapeHash 关联 profile 数据;但如果你只查单条 explain(),永远看不到“这类查询整体是否拖垮系统”。











