首页 > 编程笔记 > MongoDB 阅读:5

MongoDB复合索引详解(附带实例)

MongoDB 数据库中的复合索引从集合中每个文档的两个或多个字段中收集数据,并对其进行排序操作。数据先按索引中的第一个字段分组,再按每个后续字段分组。对经常查询的字段进行索引,可以提高实现覆盖查询的可能性。

复合查询是指可使用某一索引而不必检查任何文档便可完成的查询,使用此类索引可大幅提升性能。设计人员想要创建复合索引,需要使用以下代码:
db.<collection>.createIndex({
  <field1>: <sortOrder>,
  <field2>: <sortOrder>,
  <fieldN>: <sortOrder>
})
如果应用程序重复运行包含多个字段的查询,则可以创建复合索引来提高查询性能。对经常查询的字段使用复合索引,可以增加覆盖这些查询的机会。复合查询是可以完全使用索引进行的查询,而无须检查任何文档,这样可以优化查询性能。

关于复合索引的技术细节和使用限制,具体说明如下:
请参考如下关于索引的代码示例:
{ "item": 1, "location": 1, "price": 1 }
上述索引具有如下索引前缀:
MongoDB 可以使用复合索引来支持对这些字段组合的查询:
MongoDB 还可以使用索引来支持对 item 和 price 字段的查询,因为 item 字段对应于前缀。但是,只有索引中的 item 字段可以支持此查询。查询不能使用 location 后面的 price 字段。索引字段按顺序解析,如果查询省略了索引前缀,将无法使用该前缀后面的任何索引字段。

MongoDB 无法使用复合索引来支持对以下这些字段组合的查询:
如果没有 item 字段,则以上的字段组合都不对应前缀索引。

MongoDB创建复合索引

MongoDB复合索引是包含对多个字段的引用的索引。复合索引可以提高对索引中的字段或索引前缀中的字段的查询性能。为常用查询字段建立索引,可以增加查询覆盖的机会,这表明 MongoDB 可以完全利用索引来满足查询需求,而无须检查文档。

设计人员要创建复合索引,可以使用 db.collection.createIndex() 方法:
db.<collection>.createIndex({
  <field1>: <sortOrder>,
  <field2>: <sortOrder>,
  <fieldN>: <sortOrder>
})
注意,在单个复合索引中,限制最多仅可以指定 32 个字段。

在下面的应用示例中,创建了一个 students 集合,其中包含以下文档:
db.students.insertMany([
  {
    name: "king",
    gpa: 3.8,
    location: { city: "xc", state: "PEK" }
  },
  {
    name: "tina",
    gpa: 3.6,
    location: { city: "PD", state: "SH" }
  }
])

然后,创建一个包含 name 和 gpa 字段的复合索引,具体代码如下:
db.students.createIndex({
  name: 1,
  gpa: -1
})
在上面的代码中,name 参数上的索引是升序(1),gpa 参数上的索引为降序(-1)。基于上述索引,支持下面的查询方式:
db.students.find({ name: "king", gpa: 3.8 })
db.students.find({ name: "tina" })
注意,在使用索引查询时,不支持仅对 gpa 字段进行查询,因为 gpa 不是索引前缀的一部分。

MongoDB复合索引排序顺序

MongoDB 索引会按升序(1)或降序(-1)存储对字段的引用。对于复合索引,排序顺序可以决定索引是否支持排序操作。

复合索引支持与索引的排序顺序或索引的反向排序顺序匹配的排序操作。如果索引中的排序顺序与查询中的排序顺序匹配,则复合索引可以提高排行榜的性能。

在下面的应用示例中,创建了一个 leaderboard 集合,其中包含以下文档:
db.leaderboard.insertMany([
  {
    score: 90,
    username: "king",
    date: ISODate("2024-09-01T00:00:00Z")
  },
  {
    score: 85,
    username: "tina",
    date: ISODate("2024-09-02T00:00:00Z")
  },
  {
    score: 95,
    username: "cici",
    date: ISODate("2024-09-03T00:00:00Z")
  },
  {
    score: 80,
    username: "king",
    date: ISODate("2024-09-04T00:00:00Z")
  },
  {
    score: 75,
    username: "tina",
    date: ISODate("2024-09-05T00:00:00Z")
  }
])
查询 leaderboard 集合返回排行榜结果,具体如下:
db.leaderboard.find().sort( { score: -1, username: 1 } )
输出结果如下:
{ _id: ObjectId("6322..."), score: 75, username: 'tina', date: ISODate("2024-09-05T00:00:00.000Z") }
{ _id: ObjectId("6322..."), score: 80, username: 'king', date: ISODate("2024-09-04T00:00:00.000Z") }
{ _id: ObjectId("6322..."), score: 85, username: 'tina', date: ISODate("2024-09-02T00:00:00.000Z") }
{ _id: ObjectId("6322..."), score: 90, username: 'king', date: ISODate("2024-09-01T00:00:00.000Z") }
{ _id: ObjectId("6322..."), score: 95, username: 'cici', date: ISODate("2024-09-03T00:00:00.000Z") }
输出结果先按分数降序进行排序,然后按用户名升序(字母顺序)进行排序。

相关文章