上一篇把索引讲成了 WiredTiger 里的独立 B-tree,回答了「索引是什么、为什么查得快写得慢」。但那只解决了索引的「共性」,没解决最实际的问题:我的查询到底该建哪种索引。
MongoDB 的索引不是一个类型,而是一组。createIndex 的参数不同,建出来的是完全不同的 B-tree,服务的查询形状也完全不同。把 TTL 索引当成普通单键用、把哈希索引用在需要范围查询的字段上,都是常见的「建了等于没建」。这一篇把 MongoDB 的主要索引类型铺开,讲清楚每一种服务什么查询、有什么坑。
先把机制边界说清楚
虽然所有索引在底层都是 WiredTiger 的 B-tree,但它们的键构造方式不同,这决定了能加速什么查询:
- 单键 / 复合:键就是字段值本身(或字段值组合),支持等值、范围、排序。
- 多键:字段是数组时,对每个元素建一条目,支持「数组包含」查询。
- 地理(2dsphere/2d):键是坐标的网格编码,支持空间距离和区域查询。
- 全文(text):键是分词后的词项,支持
$text关键词搜索。 - TTL:在单键基础上挂一个过期时间,文档到期自动删除。
- 哈希(hashed):键是字段值的哈希,主要用于分片均匀打散。
- 通配符(wildcard):对动态字段名建索引,应对「字段名不固定」的场景。
理解「键怎么构造」是选对索引类型的前提,因为查询必须能匹配索引的键构造方式,优化器才会选用它。
七种索引类型总览
把七种索引的适用场景画成一张速查图,选型时按查询形状对号入座。
单键与复合:覆盖 80% 场景的主力
单键索引是最基础的形态,{ field: 1 }。它服务等值、范围和基于该字段的排序。绝大多数业务查询都从单键起步——userId、orderId、createdAt 这些高频查询字段,第一版就该建单键。
复合索引是单键的扩展,{ a: 1, b: 1, c: 1 },服务多条件联合查询。它的关键不在「能不能建」,而在「字段顺序」——顺序决定了它能加速哪些查询。比如 { a: 1, b: 1 } 能服务 {a: 1, b: 2},也能服务 {a: 1}(前缀匹配),但不能单独服务 {b: 2}(缺了前导字段 a)。复合索引的字段顺序是 ESR 原则的核心,下一篇专门展开。
一个常见的误区是「给每个查询字段都建单键」。{a:1} 和 {b:1} 两个单键,并不能服务 {a:1, b:2} 这种联合查询——优化器最多用一个,另一个用不上。联合查询必须靠复合索引,单键索引之间不能「拼」。
多键索引:数组的便利与代价
当索引字段是数组时,MongoDB 自动建成多键索引:对数组里每个元素建一条目。这让 {tags: "go"} 这种「数组包含某元素」的查询能走索引。
代价是索引体积。一个文档的 tags 有 10 个元素,多键索引里就有 10 条目指向这个文档。数组越大,索引膨胀越严重。更隐蔽的是,多键索引不能做覆盖查询(因为索引条目和文档不是一一对应),且复合索引里如果有一个字段是数组,整个索引就变成多键索引。
经验:给数组建索引前,先评估数组的平均长度。标签类(几个元素)没问题,动态流、评论列表这类(成百上千元素)建多键索引就是埋雷。
地理与全文:专用形状
2dsphere 索引服务于地理空间查询。它把坐标编码成网格,支持 $near(附近)、$geoWithin(区域内)、$geoIntersects(相交)。LBS 类业务(附近的人、附近的店)离不开它。注意它要求坐标是 GeoJSON 格式或 legacy 坐标对,建索引时要指定 2dsphere。
**全文索引(text)**服务于关键词搜索,{ content: "text" }。它对字段分词后建倒排。但 MongoDB 内置的全文索引能力有限:中文分词支持弱(默认按空格/标点分词)、不支持复杂相关性排序、不能高亮。如果搜索是核心功能,应该评估 Atlas Search(基于 Lucene)或外接 Elasticsearch,而不是硬用 MongoDB 的 text 索引。
TTL:让过期自动发生
TTL 索引是个被低估的好东西。它在单键索引上挂一个过期时间,文档到期后由后台任务(每 60 秒扫一次)自动删除。会话、验证码、短期日志、缓存这类「有时效」的数据,用 TTL 索引比靠应用定时删干净得多。
// 两种写法
db.sessions.createIndex({ lastAccess: 1 }, { expireAfterSeconds: 1800 }) // 相对过期
db.codes.createIndex({ expireAt: 1 }, { expireAfterSeconds: 0 }) // 绝对过期时间
两个要点:TTL 删除是后台批量扫,不精确(最多延迟 60 秒),不能用于强时效场景;TTL 只在单字段 date 上有效,且不能和复合索引混用(要复合 + TTL 得用 5.0+ 的 clustered collection 配合)。
哈希与通配符:特殊用途
哈希索引{ field: "hashed" }把字段值算个哈希当键。它主要用于分片键——让数据均匀打散到各个分片,避免热点。代价是哈希索引不支持范围查询,只能做等值。所以「这个字段既要分片又要范围查」时,哈希索引就不够用,要权衡。
通配符索引{ "$**": 1 }或{ "meta.$**": 1 }对动态字段名建索引,应对「字段名不固定」的场景(比如用户自定义属性)。它是个强力但昂贵的工具,索引体积大、行为复杂,除非真的有动态 schema 需求,否则不要碰。
判断框架
把七种索引收敛成几条选型原则:
- 等值/范围查单字段 → 单键,80% 场景够用。
- 多条件联合 + 排序 → 复合索引,按 ESR 原则排字段顺序。
- 数组包含查询 → 多键,先评估数组平均长度。
- 有时效的数据 → TTL,让过期自动发生。
- 地理空间 → 2dsphere;关键词搜索 → 先评估 Atlas Search,别硬用 text 索引。
- 分片要均匀 → 哈希,但牺牲范围查询;动态字段名 → 通配符,慎用。
一个反复出现的判断点:建索引前先确认查询形状能匹配索引的键构造方式。建错类型的索引,是 MongoDB 里最常见的「花了存储和写入成本却没收益」的负债。下一篇会深入复合索引的字段顺序,把 ESR 原则讲透。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 MongoDB」,欢迎关注公众号 「十三Tech」。后续会按索引优化、存储引擎、高可用和分片集群这条线更新。

