管理 Azure Cosmos DB API for MongoDB 中的索引编制

适用于:Azure Cosmos DB API for MongoDB

Azure Cosmos DB API for MongoDB 利用 Azure Cosmos DB 的核心索引管理功能。 本文重点介绍了如何使用 Azure Cosmos DB API for MongoDB 添加索引。 索引是一种专用的数据结构,可使数据的查询速度大概提升一个量级。

适用于 MongoDB 服务器 3.6 及更高版本的索引编制功能

Azure Cosmos DB API for MongoDB 服务器版本 3.6+ 会自动为 _id 字段和分片键(仅在分片集合中)编制索引。 API 会自动强制确保每个分片密钥的 _id 字段的唯一性。

API for MongoDB 的行为不同于 Azure Cosmos DB SQL API,后者默认情况下会对所有字段编制索引。

编辑索引策略

建议在 Azure 门户的数据资源管理器中编辑索引策略。 可以在数据资源管理器中的索引策略编辑器中添加单个字段和通配符索引:

Indexing policy editor

注意

无法使用数据资源管理器中的索引策略编辑器来创建复合索引。

索引类型

单个字段

只能对任何单个字段创建索引。 单字段索引的排序顺序并不重要。 以下命令对字段 name 创建索引:

db.coll.createIndex({name:1})

可以在 Azure 门户的 name 上创建相同的单个字段索引:

Add name index in indexing policy editor

在适用的情况下,一个查询将使用多个单字段索引。 对于每个集合,最多可以创建 500 个单字段索引。

复合索引(MongoDB 服务器版本 3.6+)

在 API for MongoDB 中,如果查询需要能够同时对多个字段排序,则需要复合索引。 对于包含多个不需要排序的筛选器的查询,请创建多个单字段索引而不是一个复合索引,以便节省索引编制成本。

复合索引或复合索引中每个字段的单字段索引会导致在查询中进行筛选的性能相同。

注意

不能基于嵌套属性或数组创建复合索引。

以下命令对字段 nameage 创建复合索引:

db.coll.createIndex({name:1,age:1})

可以使用复合索引来同时对多个字段进行高效排序,如以下示例中所示:

db.coll.find().sort({name:1,age:1})

还可以使用前面的复合索引,在一个查询中对所有字段按降序进行高效排序。 下面是一个示例:

db.coll.find().sort({name:-1,age:-1})

但是,复合索引中的路径顺序必须与查询完全匹配。 下面是一个需要其他复合索引的查询示例:

db.coll.find().sort({age:1,name:1})

注意

复合索引仅用于对结果进行排序的查询。 对于包含多个不需要排序的筛选器的查询,请创建多个单字段索引。

多键索引

Azure Cosmos DB 创建多键索引来为数组中存储的内容编制索引。 如果为带有数组值的字段编制索引,则 Azure Cosmos DB 会自动为数组中的每个元素编制索引。

空间索引

许多地理空间运算符可受益于地理空间索引。 Azure Cosmos DB API for MongoDB 目前支持 2dsphere 索引。 该 API 尚不支持 2d 索引。

下面是对 location 字段创建地理空间索引的示例:

db.coll.createIndex({ location : "2dsphere" })

文本索引

Azure Cosmos DB API for MongoDB 目前支持文本索引。 要对字符串运行文本搜索查询,应使用 Azure 认知搜索与 Azure Cosmos DB 的集成。

通配符索引

可以使用通配符索引来支持针对未知字段的查询。 假设你有一个包含有关家庭的数据的集合。

以下是该集合中的示例文档的一部分:

"children": [
   {
     "firstName": "Henriette Thaulow",
     "grade": "5"
   }
]

以下是另一个示例,这次在 children 中有一组稍微不同的属性:

"children": [
    {
     "familyName": "Merriam",
     "givenName": "Jesse",
     "pets": [
         { "givenName": "Goofy" },
         { "givenName": "Shadow" }
         ]
   },
   {
     "familyName": "Merriam",
     "givenName": "John",
   }
]

在此集合中,文档可以拥有许多不同的可能属性。 如果要为 children 数组中的所有数据编制索引,则有两个选择:为每个单独的属性创建单独的索引,或为整个 children 数组创建一个通配符索引。

创建通配符索引

以下命令在 children 内的任何属性上创建通配符索引:

db.coll.createIndex({"children.$**" : 1})

与在 MongoDB 中不同,通配符索引可以在查询谓词中支持多个字段。 如果使用一个通配符索引,而不是为每个属性创建单独的索引,查询性能不会有差异。

可以使用通配符语法创建以下索引类型:

  • 单个字段
  • 地理空间

为所有属性编制索引

在所有字段上创建通配符索引的方法如下:

db.coll.createIndex( { "$**" : 1 } )

还可以使用 Azure 门户中的数据资源管理器创建通配符索引:

Add wildcard index in indexing policy editor

注意

如果你刚开始开发,强烈建议你在所有字段上使用通配符索引。 这可以简化开发,并使优化查询更加容易。

具有多个字段的文档可能会在写入和更新时产生较高的请求单位 (RU) 费用。 因此,如果有写入密集型工作负荷,则应选择单独的索引路径,而不要使用通配符索引。

限制

通配符索引不支持以下任何索引类型或属性:

  • 复合
  • TTL
  • 唯一

与在 MongoDB 中不同,在 Azure Cosmos DB API for MongoDB 中,不能使用通配符索引进行以下操作:

  • 创建包含多个特定字段的通配符索引

    db.coll.createIndex(
      { "$**" : 1 },
      { "wildcardProjection " :
          {
             "children.givenName" : 1,
             "children.grade" : 1
          }
      }
    )
    
  • 创建排除多个特定字段的通配符索引

    db.coll.createIndex(
      { "$**" : 1 },
      { "wildcardProjection" :
          {
             "children.givenName" : 0,
             "children.grade" : 0
          }
      }
    )
    

作为替代方法,你可以创建多个通配符索引。

索引属性

对于处理线路协议版本 4.0 的帐户和处理更早版本的帐户,以下操作常用。 还可以详细了解支持的索引和已编制索引的属性

唯一索引

对于编制了索引的字段,唯一索引用于确保这些字段的同一值不会存在于两个或两个以上的文档中。

以下命令对字段 student_id 创建唯一索引:

globaldb:PRIMARY> db.coll.createIndex( { "student_id" : 1 }, {unique:true} )
{
    "_t" : "CreateIndexesResponse",
    "ok" : 1,
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 4
}

对于分片的集合,必须提供分片(分区)键才能创建唯一索引。 换言之,在分片集合上的所有唯一索引都是复合索引,其中的一个字段是分区键。

以下命令创建一个分片的集合 coll(分片键为 university),该集合具有 student_iduniversity 字段上的唯一索引:

globaldb:PRIMARY> db.runCommand({shardCollection: db.coll._fullName, key: { university: "hashed"}});
{
    "_t" : "ShardCollectionResponse",
    "ok" : 1,
    "collectionsharded" : "test.coll"
}
globaldb:PRIMARY> db.coll.createIndex( { "university" : 1, "student_id" : 1 }, {unique:true});
{
    "_t" : "CreateIndexesResponse",
    "ok" : 1,
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 3,
    "numIndexesAfter" : 4
}

在前面的示例中,省略 "university":1 子句将返回包含以下消息的错误:

cannot create unique index over {student_id : 1.0} with shard key pattern { university : 1.0 }

限制

在集合为空时,需要创建唯一索引。

对于未使用 Synapse Link 或连续备份的帐户,预览版可以支持针对现有包含数据的集合进行唯一索引。

唯一部分索引

可以通过在索引中指定 partialFilterExpression 以及“unique”约束来创建唯一部分索引。 这会导致唯一约束仅应用于满足指定筛选表达式的文档。

对于不满足指定条件的文档,唯一约束将无效。 因此,不会阻止将其他文档插入到集合中。

Cosmos DB API for MongoDB 版本 3.6 及更高版本支持此功能。

若要从 Mongo Shell 创建唯一部分索引,请使用具有“partialFilterExpression”选项和“unique”约束的命令 db.collection.createIndex()。 partialFilterExpression 选项接受使用以下内容指定筛选器条件的 json 文档:

  • 相等表达式(即“字段: 值”或使用 $eq 运算符)、*'$exists: true' 表达式,
  • $gt、$gte、$lt、$lte 表达式,
  • $type 表达式,
  • 仅限顶层的 $and 运算符

以下命令对集合 books 创建索引,它指定字段 title 上的唯一约束和部分筛选表达式 rating: { $gte: 3 }

db.books.createIndex( 
   { title: 1 }, 
   { unique: true, partialFilterExpression: { rating: { $gte: 3 } } } 
) 

若要使用 Mongo Shell 删除部分唯一索引,请使用命令 getIndexes() 列出集合中的索引。 然后使用以下命令删除索引:

db.books.dropIndex("indexName")

TTL 索引

若要在特定集合中启用文档过期,需创建生存时间 (TTL) 索引。 TTL 索引是具有 expireAfterSeconds 值的 _ts 字段上的索引。

示例:

globaldb:PRIMARY> db.coll.createIndex({"_ts":1}, {expireAfterSeconds: 10})

上面的命令会在 db.coll 集合中删除过去 10 秒内未修改的任何文档。

注意

_ts 字段是特定于 Azure Cosmos DB 的字段,不可从 MongoDB 客户端访问。 它是一个保留(系统)属性,其中包含文档上次进行修改时的时间戳。

跟踪索引进度

Azure Cosmos DB API for MongoDB 版本 3.6+ 支持使用 currentOp() 命令来跟踪数据库实例上的索引进度。 此命令返回一个文档,其中包含有关数据库实例上正在进行的操作的信息。 可使用 currentOp 命令跟踪本机 MongoDB 中所有正在进行的操作。 在 Azure Cosmos DB API for MongoDB 中,此命令仅支持跟踪索引操作。

下面这些示例演示如何使用 currentOp 命令来跟踪索引进度:

  • 获取集合的索引进度:

    db.currentOp({"command.createIndexes": <collectionName>, "command.$db": <databaseName>})
    
  • 获取数据库中所有集合的索引进度:

    db.currentOp({"command.$db": <databaseName>})
    
  • 获取 Azure Cosmos 帐户中所有数据库和集合的索引进度:

    db.currentOp({"command.createIndexes": { $exists : true } })
    

索引进度输出示例

索引进度详细信息显示当前索引操作的进度百分比。 以下示例显示索引进度的不同阶段的输出文档格式:

  • 如果针对“foo”集合与“bar”数据库执行了索引操作,且该操作已完成 60%,那么该操作将有以下输出文档。 Inprog[0].progress.total 字段将 100 显示为目标完成百分比。

    {
        "inprog" : [
        {
                ………………...
                "command" : {
                        "createIndexes" : foo
                        "indexes" :[ ],
                        "$db" : bar
                },
                "msg" : "Index Build (background) Index Build (background): 60 %",
                "progress" : {
                        "done" : 60,
                        "total" : 100
                },
                …………..…..
        }
        ],
        "ok" : 1
    }
    
  • 如果索引操作刚刚针对“foo”集合与“bar”数据库启动,那么在进度达到可度量的级别之前,输出文档可能会一直显示 0% 进度。

    {
        "inprog" : [
        {
                ………………...
                "command" : {
                        "createIndexes" : foo
                        "indexes" :[ ],
                        "$db" : bar
                },
                "msg" : "Index Build (background) Index Build (background): 0 %",
                "progress" : {
                        "done" : 0,
                        "total" : 100
                },
                …………..…..
        }
        ],
       "ok" : 1
    }
    
  • 正在进行的索引操作完成后,输出文档将显示 inprog 操作为空。

    {
      "inprog" : [],
      "ok" : 1
    }
    

后台索引更新

无论为 Background 索引属性指定了什么值,索引更新始终会在后台完成。 由于索引更新操作使用请求单位 (RU) 的优先级低于其他数据库操作,因此,索引更改不会导致写入、更新或删除操作无法正常进行。

添加新索引时,对读取可用性没有影响。 索引转换完成之后,查询将只利用新索引。 在索引转换过程中,查询引擎将继续使用现有的索引,因此,在索引转换过程中,你将观察到,读取性能类似于在启动索引更改之前观察到的情况。 添加新索引时,也不会有查询结果不完整或不一致的风险。

删除索引并立即运行在删除的索引上有筛选器的查询时,在索引转换完成之前,结果可能不一致且不完整。 如果删除索引,则当查询对这些新删除的索引进行筛选时,查询引擎将无法提供一致或完整的结果。 大多数开发人员不会在删除索引后立即尝试查询它们,因此,在实践中,这种情况不太可能发生。

注意

可以跟踪索引进度

ReIndex 命令

reIndex 命令会重新创建集合上的所有索引。 在极少数情况下,可以运行 reIndex 命令来解决集合中的查询性能或其他索引问题。 如果在编制索引时遇到问题,建议的方法是使用 reIndex 命令重新创建索引。

可以使用以下语法运行 reIndex 命令:

db.runCommand({ reIndex: <collection> })

可使用以下语法来检查运行 reIndex 命令能否提高集合中的查询性能:

db.runCommand({"customAction":"GetCollection",collection:<collection>, showIndexes:true})

示例输出:

{
        "database" : "myDB",
        "collection" : "myCollection",
        "provisionedThroughput" : 400,
        "indexes" : [
                {
                        "v" : 1,
                        "key" : {
                                "_id" : 1
                        },
                        "name" : "_id_",
                        "ns" : "myDB.myCollection",
                        "requiresReIndex" : true
                },
                {
                        "v" : 1,
                        "key" : {
                                "b.$**" : 1
                        },
                        "name" : "b.$**_1",
                        "ns" : "myDB.myCollection",
                        "requiresReIndex" : true
                }
        ],
        "ok" : 1
}

如果 reIndex 可提高查询性能,则 requiresReIndex 为 true。 如果 reIndex 不能提高查询性能,则省略此属性。

迁移带索引的集合

目前,仅当集合不包含文档时才能创建唯一索引。 常用 MongoDB 迁移工具会尝试在导入数据后创建唯一索引。 若要规避此问题,可以手动创建相应的集合和唯一索引,而不是允许迁移工具尝试。 (可以在命令行中使用 --noIndexRestore 标志来为 mongorestore 实现此行为。)

适用于 MongoDB 版本 3.2 的索引编制功能

对于与 MongoDB Wire Protocol 版本 3.2 兼容的 Azure Cosmos DB 帐户,可用的索引编制功能和默认值是不同的。 可以检查帐户的版本升级到版本 3.6

如果使用的是版本 3.2,请阅读此部分,其中概述了版本 3.2 与版本 3.6+ 之间的重要差别。

删除默认索引(版本 3.2)

与 Azure Cosmos DB API for MongoDB 版本 3.6+ 不同,版本 3.2 默认会为每个属性编制索引。 可以使用以下命令删除集合 (coll) 的这些默认索引:

> db.coll.dropIndexes()
{ "_t" : "DropIndexesResponse", "ok" : 1, "nIndexesWas" : 3 }

删除默认索引后,可以像在版本 3.6+ 中那样添加更多索引。

复合索引(版本 3.2)

复合索引包含对文档多个字段的引用。 若要创建复合索引,请升级到版本 3.6 或 4.0

通配符索引(版本 3.2)

若要创建通配符索引,请升级到版本 4.0 或 3.6

后续步骤