Azure DocumentDB 中的索引最佳实践

可查询字段应始终创建索引

基于谓词和聚合的读取操作会参考索引中的相应筛选条件。 如果没有索引,数据库引擎将执行文档扫描以检索匹配的文档。 扫描始终昂贵,随着集合中的数据量的增长,扫描成本会逐渐增加。 为了获得最佳查询性能,应始终为所有可查询字段创建索引。

避免创建不必要的索引,并避免默认对所有字段编制索引。

只应为可查询字段创建索引。 仅当查询模式不可预知时,才应使用通配符索引,其中文档结构中的任何字段都可以是查询筛选器的一部分。

小窍门

默认情况下,Azure DocumentDB 仅对_id字段编制索引。 默认情况下不会为所有其他字段编制索引。 应预先规划要编制索引的字段,以最大程度地提高查询性能,同时尽量减少由于索引字段过多而对写入产生的影响。

首次插入新文档或更新或删除现有文档时,索引中的每个指定字段也会更新。 如果索引策略包含大量字段(或文档中的所有字段),则服务器在更新相应索引时会消耗更多资源。 大规模运行时,只有可查询字段才应编制索引,而查询谓词中未使用的所有剩余字段都应从索引中排除。

用于高效数据引入的索引策略

若要将大型工作负荷迁移到 Azure DocumentDB,建议在数据加载后创建索引,以便高效执行。 这可以显著减少写入开销,最大程度地减少资源消耗,并加速数据引入性能。 在批量引入期间维护索引可能会减慢插入速度,因为每个写入作都必须更新所有适用的索引。

对于针对历史数据创建的多个索引,请为每个字段发出非阻塞 createIndex 命令

并非总是可以提前规划所有查询模式,特别是随着应用程序需求的发展。 更改应用程序需要不可避免地需要将字段添加到具有大量历史数据的群集上的索引中。

在这种情况下,应异步发出每个 createIndex 命令,而无需等待来自服务器的响应。

注释

默认情况下,Azure DocumentDB 仅在索引完全在历史数据上生成后才响应createIndex操作。 根据群集的大小和引入的数据量,这可能需要一段时间,并且看起来服务器没有响应 createIndex 命令。

如果通过 Mongo Shell 发出 createIndex 命令,请使用 Ctrl + C 中断命令以停止等待响应并发出下一组作。

注释

在发出 createIndex 命令后,使用 Ctrl + C 中断该命令不会终止服务器上的索引生成作。 它只是阻止 Shell 等待来自服务器的响应,而服务器以异步方式继续基于现有文档生成索引。

为具有多个字段谓词的查询创建复合索引

复合索引应在以下方案中使用:

  • 含有多个字段筛选条件的查询
  • 支持对多个字段进行筛选并按升序或降序对一个或多个字段排序的查询

请考虑“cosmicworks”数据库和“employee”集合中的以下文档

{
    "firstName": "Steve",
    "lastName": "Smith",
    "companyName": "Microsoft",
    "division": "Azure",
    "subDivision": "Data & AI",
    "timeInOrgInYears": 7
}

请考虑以下查询,查找在组织工作超过五年的所有姓氏为“Smith”的员工:

db.employee.find({"lastName": "Smith", "timeInOrgInYears": {"$gt": 5}})

“lastName”和“timeInOrgInYears”上的复合索引可优化此查询:

use cosmicworks;
db.employee.createIndex({"lastName" : 1, "timeInOrgInYears" : 1})

跟踪 createIndex 操作的状态

当添加索引且需要对历史数据进行索引时,可以使用 db.currentOp()来跟踪索引构建操作的进度。

请考虑此示例来跟踪“cosmicworks”数据库的索引进度。

use cosmicworks;
db.currentOp()

当 createIndex 操作正在进行中时,响应如下所示。

{
  "inprog": [
    {
      "shard": "defaultShard",
      "active": true,
      "type": "op",
      "opid": "30000451493:1719209762286363",
      "op_prefix": 30000451493,
      "currentOpTime": "2024-06-24T06:16:02.000Z",
      "secs_running": 0,
      "command": { "aggregate": "" },
      "op": "command",
      "waitingForLock": false
    },
    {
      "shard": "defaultShard",
      "active": true,
      "type": "op",
      "opid": "30000451876:1719209638351743",
      "op_prefix": 30000451876,
      "currentOpTime": "2024-06-24T06:13:58.000Z",
      "secs_running": 124,
      "command": { "createIndexes": "" },
      "op": "workerCommand",
      "waitingForLock": false,
      "progress": {},
      "msg": ""
    }
  ],
  "ok": 1
}

默认启用大型索引键

即使文档不包含具有大量字符的键,或者文档不包含多个级别的嵌套,指定大型索引键可确保涵盖这些方案。 现在,大型索引键是引擎的默认行为。

请参阅此示例以在“cosmicworks”数据库中的“large_index_coll”集合上启用大索引键功能。

use cosmicworks;
db.runCommand(
{
 "createIndexes": "large_index_coll",
 "indexes": [
    {
        "key": { "ikey": 1 },
        "name": "ikey_1",
        "enableLargeIndexKeys": true
    }
    ]
})

使用阻塞选项,将索引构建优先于新的写入操作

对于在加载数据之前应创建索引的方案,应使用阻止选项来阻止传入写入,直到索引生成完成。

在迁移实用工具中,在数据写入开始之前,在空集合上创建索引时,设置 { "blocking": true } 非常有用。

示例说明如何在“cosmicworks”数据库中的“employee”集合上使用阻止选项来创建索引:

use cosmicworks;
db.runCommand({
  createIndexes: "employee",
  indexes: [{"key":{"name":1}, "name":"name_1"}],
  blocking: true
})

查看 文本索引,以便高效搜索和查询基于文本的数据。

后续步骤