多键索引
本教程共 50 篇 · 第 42 篇 · 更新于 2026-07-30 · 约 4 分钟阅读
42. 多键索引
本节目标:理解数组字段为何会自动产生多键索引,以及复合索引里对数组字段的数量限制。
普通索引一个文档对应一条索引项。但字段如果是数组(Array),一个文档里就有多个值。MongoDB 为此准备了多键索引(Multikey Index)。
42.1 数组字段自动变成多键索引
给数组字段建索引时,MongoDB 会为数组里每一个元素都建一条索引项。这种索引就叫多键索引。它不需要你额外声明,自动生效。
我们的 users 有 tags: [],products 也有 tags: []。直接建索引:
// 给 users 的 tags 建索引,MongoDB 自动转为多键索引
test> db.users.createIndex({ tags: 1 })
// 给 products 的 tags 建索引
test> db.products.createIndex({ tags: 1 })
插入带多个标签的文档:
test> db.users.insertOne({
_id: 1, name: "小明", age: 28, city: "杭州",
tags: ["vip", "early", "gamer"], registered_at: new Date()
})
这条文档在 tags 索引里会生成三条索引项,分别对应 vip、early、gamer。查任意一个标签都能命中。
// 命中多键索引,找出带 gamer 标签的用户
test> db.users.find({ tags: "gamer" }).explain("queryPlanner")
Tip多键索引让「数组里有没有这个值」的查询变得很快。标签、分类、成员列表这类场景都靠它。
42.2 判断是不是多键索引
用 getIndexes() 看索引的 multikey 字段:
test> db.users.getIndexes()
返回里某索引带 "multikey" : true,就说明它是多键索引。这是 MongoDB 自动打的标记。
42.3 复合索引中的多键限制
多键索引能和其他字段组合成复合索引,但有一条硬限制:一个复合索引里,最多只能有一个字段是数组。
// 合法:tags 是数组,city 是标量,复合没问题
test> db.products.createIndex({ category: 1, tags: 1 })
我们的 users 既带 tags(数组),也可能带 addresses(数组)。若同一文档里两个字段都是数组(平行数组),把它们一起建进复合索引,MongoDB 会报 cannot index parallel arrays。
// 先插一个 tags 和 addresses 都是数组的文档
test> db.users.insertOne({
_id: 99, name: "测试", age: 20, city: "杭州",
tags: ["vip"],
addresses: [{ tag: "家", city: "杭州", detail: "文一西路 1 号" }],
registered_at: new Date()
})
// 报错:同一文档两个字段都是数组,无法共建复合索引
test> db.users.createIndex({ tags: 1, addresses: 1 })
Warning复合多键索引最多容纳一个数组字段,指的是「同一文档里两个字段都为数组」这种平行数组。设计索引时先想清哪个是数组,别把两个数组硬塞一起。
42.4 数组内匹配:$elemMatch 与索引
若数组元素是文档(如 orders.items),想匹配「同一元素内多个条件」,用 $elemMatch。
// 匹配 items 里同一元素同时满足 product 和 qty
test> db.orders.find({
items: { $elemMatch: { product: "机械键盘", qty: { $gte: 1 } } }
})
给 items.product 建索引可加速这类查询:
test> db.orders.createIndex({ "items.product": 1 })
Note多键索引对「数组元素是文档」同样有效,索引键写成
"items.product"这种点号路径即可。
42.5 小结
数组字段建索引会自动生成多键索引,每个元素一条索引项。复合多键索引最多含一个数组字段,这是设计时必须守住的边界。