MongoDB 4.2版本引入了通配符索引(Wildcard Index)这一特性,初衷是为了解决Schema不固定的集合中字段无法提前建索引的问题。它可以用一个索引覆盖所有字段名,或者通过$**模式匹配特定前缀的字段。然而灵活性是有代价的,当数据库记录故障码1440并提示通配符索引性能下降时,说明这个代价已经大到影响业务了。本文将从原理层面剖析1440的成因,并给出一套可落地的排查与优化方案。

故障码1440的底层机制是什么
要理解1440,先要理解通配符索引的存储结构。普通索引的B树中,键就是字段的值本身,而通配符索引的B树键是一个复合结构,包含字段名和字段值的组合。举个例子,文档{ "price": 100 }在通配符索引中的键大致是("price", 100)这样的形式。这意味着同一个集合中所有被索引字段的键都会挤进同一棵B树。
当集合中的字段名种类非常多时,这棵B树的扇出会急剧恶化。故障码1440本质上是一个性能预警信号,通常伴随执行计划中扫描键数量(index keys examined)与返回文档数量的比例严重失衡。你可以通过慢查询日志观察到典型的特征:普通索引扫描比值接近1:1,而触发1440的通配符索引扫描,比值可能达到几百比一甚至更高。
另一个容易忽视的机制是通配符索引对嵌套文档的处理。默认情况下,{"$**": 1}会递归展开所有嵌套层级,一个深层嵌套的文档可能在索引中生成大量键。数据写入时这些键都要维护,查询时也要逐个判断,CPU和IO开销随嵌套深度线性增长。
哪些场景容易触发通配符索引性能下降
第一个典型场景是Schema极度灵活的集合。比如日志系统、用户行为追踪这类业务,每条文档都可能带不同的字段名。字段名种类突破几万个之后,通配符索引的B树高度增加,缓存命中率下降,每次查询需要更多次的磁盘IO。此时即便只查询一个固定字段,效率也远不如为该字段单独建索引。
第二个场景是查询条件不够精准。通配符索引只有在查询明确指定了具体字段路径时才能被高效利用。下面这两种写法差异巨大:
// 写法一:明确指定字段,可以精准定位到 ("price", 100) 这个键范围
db.products.find({ "price": 100 })
// 写法二:无法使用通配符索引,只能全表扫描
db.products.find({ "$or": [
{ "price": 100 },
{ "discount": 100 }
] })
写法二中的$or如果每个分支的字段没有独立索引支持,优化器可能放弃通配符索引。类似地,$exists、正则表达式前缀匹配等操作在通配符索引上的表现也远不如普通索引。
第三个场景是索引膨胀。数组字段在索引中会为每个数组元素生成一个键,如果集合里存在大数组,且被通配符索引覆盖,索引体积可能达到数据体积的数倍,写入放大严重,内存中的 WiredTiger 缓存被大量索引页占据,形成连锁性能衰退。
如何诊断与优化
诊断的第一步是用explain分析执行计划,重点关注几个关键指标:
db.products.find({ "price": 100 }).explain("executionStats")
在输出结果中查看totalKeysExamined与nReturned的比值,查看stage是否为IXSCAN且索引名称为通配符索引,查看executionTimeMillis是否异常偏高。同时可以借助$indexStats聚合阶段观察索引的实际使用频率:
db.products.aggregate([
{ $indexStats: {} },
{ $project: { name: 1, accesses: 1, key: 1 } }
])
如果发现某个通配符索引访问次数很低但体积很大,基本可以判定它是性能包袱。
优化方面,首先推荐缩小通配符的范围,不要对整个文档建索引,而是只对特定前缀生效,例如{"userAttributes.$**": 1}只索引userAttributes子文档下的动态字段。这样字段名空间被限制在可控范围内,B树的扇出问题大幅缓解。
其次,对高频查询字段建立独立索引。通配符索引的定位是兜底方案,核心查询路径上的字段应该有专属索引。MongoDB允许同一个字段同时被普通索引和通配符索引覆盖,优化器会自动选择代价更低的那一个。
第三,利用wildcardProjection过滤掉不需要索引的字段,尤其是排除大数组字段和长文本字段:
db.products.createIndex(
{ "$**": 1 },
{
wildcardProjection: {
"description": 0,
"commentList": 0,
"history.log": 0
}
}
)
排除这些高体积字段后,索引体积通常能下降一半以上,写入延迟也随之改善。
最后,对于已经严重膨胀的索引,最直接的办法是删除重建。在业务低峰期执行dropIndex再createIndex,注意MongoDB 4.2之后的索引构建默认使用新的构建流程,对线上读写阻塞较小,但集合较大时仍需评估窗口期。重建后结合上述的范围限定和投影过滤,才能从根本上避免1440问题复发。
MongoDB故障排查通配符索引索引性能优化修改时间:2026-09-16 09:44:32