MongoDB——部分索引

280 阅读1分钟

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

有时,只索引集合汇中一部分文档是有意义的。当我们对文档的子集加索引时,我们可以降低存储需求并降低创建和维护索引的性能成本。为此,MongoDB 支持创建部分索引。

创建索引

要创建部分索引,还是使用db.collection.createIndex()需要带有partialFilterExpression参数。

partialFilterExpression可以支持一下过滤条件:

  • 等式表达式(即field: value或使用$eq
  • $exists: true
  • $gt$gte$lt$lte
  • $type
  • $and$or
  • $in

使用

我们先看一个例子,以餐厅集合为例。

db.restaurants.insert({
   "name" : "Han Dynasty",
   "cuisine" : "Sichuan",
   "stars" : 4.4,
   "address" : {
      "street" : "90 3rd Ave",
      "city" : "New York",
      "state" : "NY",
      "zipcode" : "10003"
   }
});

我们需要查询某个城市特定的美食。我们发现服务器收到的查询中,90%以上的查询都是3.5 评分以上的餐厅。因此我们可以创建以下索引:

db.restaurants.createIndex({"address.city": 1, "cuisne": 1})
db.restaurants.createIndex({"address.city": 1, "cuisne": 1, "stars": 1})

也可以创建一个部分索引

db.restaurants.createIndex(
  { "address.city": 1, cuisine: 1 },
  { partialFilterExpression: { 'stars': { $gte: 3.5 } } }
)

当餐厅有3.5以上评分,我们才对城市和美食进行索引,而不是创建前面的复合索引。

我们有效地减少了存储索引键的数量,从而减少我们对索引空间的需求,如果我们的索引变得过大,可能会无法放入到内存中。

部分索引也可用于多键索引。使用多键索引时,会为每个数组元素创建键。如果有特别大的数组,那么将创建大量的索引键。我们可以通过稀疏索引来缓解这类问题。

稀疏索引是部分索引的特例。使用稀疏索引,我们只索引我们正在索引字段的文档,而不是创建有空值的索引键。我们可以通过创建部分索引来实现相同的结果。但是这块部分索引比稀疏索引更有表现力。使用部分索引,我们可以第一个$exists 来扫描是否存在的字段。因此建议使用部分索引而不是稀疏索引。

我们可以试着查询一下

var exp = db.restaurants.explain()
exp.find({'address.city': 'New York', cuisine: 'Sichuan', stars: { $gt: 4.0 }})
{
  queryPlanner: {
    namespace: 'test.restaurants',
    indexFilterSet: false,
    parsedQuery: {
      '$and': [
        { 'address.city': { '$eq': 'New York' } },
        { cuisine: { '$eq': 'Sichuan' } },
        { stars: { '$gt': 4 } }
      ]
    },
    queryHash: 'DAC1A4CF',
    planCacheKey: '113446E1',
    maxIndexedOrSolutionsReached: false,
    maxIndexedAndSolutionsReached: false,
    maxScansToExplodeReached: false,
    winningPlan: {
      stage: 'FETCH',
      filter: { stars: { '$gt': 4 } },
      inputStage: {
        stage: 'IXSCAN',
        keyPattern: { 'address.city': 1, cuisine: 1 },
        indexName: 'address.city_1_cuisine_1',
        isMultiKey: false,
        multiKeyPaths: { 'address.city': [], cuisine: [] },
        isUnique: false,
        isSparse: false,
        isPartial: true,
        indexVersion: 2,
        direction: 'forward',
        indexBounds: {
          'address.city': [ '["New York", "New York"]' ],
          cuisine: [ '["Sichuan", "Sichuan"]' ]
        }
      }
    },
    rejectedPlans: []
  }
}

按照预期是索引扫描,为了出发索引扫描,我们需要在查询中包含我们索引的字段。