首页 / MongoDB 入门教程 / 多键索引

MongoDB 入门教程

多键索引

本教程共 50 篇 · 第 42 篇 · 更新于 2026-07-30 · 约 4 分钟阅读

MongoDBMongoDB 入门教程多键索引数组索引复合索引限制tags

42. 多键索引

本节目标:理解数组字段为何会自动产生多键索引,以及复合索引里对数组字段的数量限制。

普通索引一个文档对应一条索引项。但字段如果是数组(Array),一个文档里就有多个值。MongoDB 为此准备了多键索引(Multikey Index)。

42.1 数组字段自动变成多键索引

给数组字段建索引时,MongoDB 会为数组里每一个元素都建一条索引项。这种索引就叫多键索引。它不需要你额外声明,自动生效。

我们的 userstags: []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 索引里会生成三条索引项,分别对应 vipearlygamer。查任意一个标签都能命中。

// 命中多键索引,找出带 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 小结

数组字段建索引会自动生成多键索引,每个元素一条索引项。复合多键索引最多含一个数组字段,这是设计时必须守住的边界。