MongoDB——复合索引扩展

116 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情

索引前缀

索引前缀是复合索引的从左侧开始的一个子集。假如我们有以下复合索引:

{ "item": 1, "location": 1, "stock": 1 }

则该索引具有以下索引前缀

{ "item": 1}
{ "item": 1, "location": 1}

对于复合索引,MongoDB可以使用复合索引的前缀来查询。因此,可以查询以下字段:

  • item
  • itemlocation
  • itemlocationstock

还可以支持对itemstock 字段的查询,因为item 字段是索引前缀。但是这样查询肯定不如只有itemstock字段对应的复合索引。**索引会按照字段顺序解析,如果查询省略了特定的索引前缀,则无法使用该前缀后面的任何索引字段。**由于中间省略了location,不能使用stock字段,只支持item字段。

还不支持以下字段查询:

  • location
  • stock
  • locationstock

前面对 person.json已经创建了last_namefirst_name的复合索引。因为我前面还创建了last_name 的单键索引,需要测试,我们先删除。再执行以下查询

db.people.dropIndex("last_name_1")
exp = db.people.explain("executionStats")
exp.find( { "last_name" : "Solomon" } )
{
  executionStats: {
    executionSuccess: true,
    nReturned: 22,
    executionTimeMillis: 0,
    totalKeysExamined: 22,
    totalDocsExamined: 22,
    executionStages: {
      stage: 'FETCH',
      nReturned: 22,
      inputStage: {
        stage: 'IXSCAN',
        nReturned: 22,
        indexName: 'last_name_1_first_name_1'
      }
    }
  }
}

可以看到扫描了22个索引键,22个文档,返回了22个文档,非常好比例。没有查看多余的索引键,执行时间页很快。

但是我们假如查询first_name ,而不是查询last_name

exp.find( { "first_name" : "Sonia" } )
{
  executionStats: {
    executionSuccess: true,
    nReturned: 8,
    executionTimeMillis: 29,
    totalKeysExamined: 0,
    totalDocsExamined: 50474,
    executionStages: {
      stage: 'COLLSCAN',
      filter: { first_name: { '$eq': 'Sonia' } },
    }
  }
}

可以看到使用了全表扫描,没有使用索引键,而且也花了很长的时间。我们不能使用这个复合索引的原因是,我们没有使用索引前缀。

我们也可以看看查询结果发现last_name是有序的,first_name可以在任意位置。first_name顺序是相对于last_name的,但是first_name本身,如果是我们要查询的,可能出现在任意位置。所以这个复合索引相对于这条查询时无效的,依然需要去进行全表扫描。

如果我们有一个复合索引,必须使用前缀才可以,否则不会使用当前索引。我们在构建复合索引的时候需要考虑清楚。如果我们有两个查询,其中一个使用作为另一个子集的字段,应该构建一个索引,其中一个查询使用索引前缀,另一个使用索引的所有字段。