你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

管理 Azure Cosmos DB 中的索引策略

在 Azure Cosmos DB 中,数据是按照为每个容器定义的索引策略编制索引的。 新建容器的默认索引策略会对任何字符串或数字强制使用范围索引。 可以使用自己的自定义索引策略替代此策略。

注意

本文所述的更新索引策略的方法仅适用于 Azure Cosmos DB for NoSQL。 了解 Azure Cosmos DB for MongoDB 中的索引编制以及 Azure Cosmos DB for Apache Cassandra 中的辅助索引。

索引策略示例

以下是一些索引策略的示例,以它们的 JSON 格式呈现。 它们以 JSON 格式在 Azure 门户上公开。 可以使用 Azure CLI 或任何 SDK 设置相同的参数。

用以有选择地排除某些属性路径的选择退出策略

{
    "indexingMode": "consistent",
    "includedPaths": [
        {
            "path": "/*"
        }
    ],
    "excludedPaths": [
        {
            "path": "/path/to/single/excluded/property/?"
        },
        {
            "path": "/path/to/root/of/multiple/excluded/properties/*"
        }
    ]
}

注意

  • 分区键(除非也是 /id)未编制索引,并且应包含在索引中。
  • 当 cosmos 帐户索引模式为consistent时,系统属性id_ts始终编制索引。
  • 系统属性 id_ts 并且不包含在容器策略的索引路径说明中。 这是设计使然,因为这些系统属性默认编制索引,因此无法禁用此行为。

用以有选择地包括某些属性路径的选择加入策略

{
    "indexingMode": "consistent",
    "includedPaths": [
        {
            "path": "/path/to/included/property/?"
        },
        {
            "path": "/path/to/root/of/multiple/included/properties/*"
        }
    ],
    "excludedPaths": [
        {
            "path": "/*"
        }
    ]
}

注意

我们通常建议使用选择退出索引策略。 Azure Cosmos DB 会主动为你的数据模型中可能添加的任何新属性进行索引。

仅在特定属性路径上使用空间索引

{
    "indexingMode": "consistent",
    "automatic": true,
    "includedPaths": [
        {
            "path": "/*"
        }
    ],
    "excludedPaths": [
        {
            "path": "/_etag/?"
        }
    ],
    "spatialIndexes": [
        {
                    "path": "/path/to/geojson/property/?",
            "types": [
                "Point",
                "Polygon",
                "MultiPolygon",
                "LineString"
            ]
        }
    ]
}

矢量索引策略示例

除了包含或排除各个属性的路径以外,还可以指定一个矢量索引。 一般情况下,每当使用 VectorDistance 系统函数测量查询矢量和矢量属性之间的相似度时,都应指定矢量索引。

注意

在继续作之前,必须启用 Azure Cosmos DB NoSQL 矢量索引和搜索

重要

矢量索引策略必须位于容器的矢量策略中定义的同一路径上。 若要了解详细信息,请参阅 容器向量策略

{
    "indexingMode": "consistent",
    "automatic": true,
    "includedPaths": [
        {
            "path": "/*"
        }
    ],
    "excludedPaths": [
        {
            "path": "/_etag/?"
        }
    ],
    "vectorIndexes": [
        {
            "path": "/vector",
            "type": "quantizedFlat"
        }
    ]
}

重要

矢量策略或向量索引目前不支持嵌套在数组内的通配符(*[])和矢量路径。

重要

目前,矢量策略和矢量索引在创建后是不可变的。 若要进行更改,请创建新的集合。

可以定义以下类型的矢量索引策略:

类型 描述 最大维度
flat 将矢量存储在与其他已索引属性相同的索引上。 505
quantizedFlat 在索引上存储之前,量化(压缩)矢量。 这可以降低延迟和吞吐量,但会牺牲一点准确度。 4096
diskANN 基于 DiskANN 创建索引,实现快速高效的近似搜索。 4096

执行矢量搜索时,索引flatquantizedFlat类型和索引类型使用 Azure Cosmos DB 的索引来存储和读取每个向量。 使用 flat 索引的矢量搜索是暴力搜索,其准确度为 100%。 但是,平面索引上的向量有 505 个维度的限制。

quantizedFlat 索引会在该索引上存储量化或压缩矢量。 使用 quantizedFlat 索引的矢量搜索也是暴力搜索,但由于矢量在添加到索引之前进行了量化,因此其准确度可能略低于 100%。 但相较于 quantized flat 索引上的矢量搜索,使用 flat 的矢量搜索应会具有更低的延迟、更高的吞吐量和更低的 RU 成本。 对于使用查询筛选器将矢量搜索缩小到相对较小的矢量集的方案,这是一个不错的选择。

diskANN 索引是一个单独的索引,专用于使用 DiskANN 为矢量定义,这是由 Microsoft Research 开发的高性能矢量索引算法套件。 DiskANN 索引可以提供一些最低延迟、每秒最高查询(QPS)和最低的 RU 成本查询,且准确度高。 但由于 DiskANN 是近似最近邻域 (ANN) 索引,因此准确度可能低于 quantizedFlatflat

索引diskANNquantizedFlat可以采用可选的索引生成参数,这些参数可用于优化适用于每个 ANN 矢量索引的准确度与延迟权衡。

  • quantizationByteSize:设置产品量化的大小(以字节为单位):Min=1,Default=dynamic(系统决定),Max=512。 设置此较大可能会导致更高的准确度矢量搜索,代价是 RU 成本较高和延迟较高。 这适用于 quantizedFlatDiskANN 索引类型。
  • indexingSearchListSize:设置索引生成构造期间要搜索的矢量数:Min=10、Default=100、Max=500。 如果设置此较大值,则会导致更高的准确性矢量搜索,代价是索引生成时间更长,矢量引入延迟较高。 这仅适用于 DiskANN 索引。

使用分片 DiskANN

分片 DiskANN 通过将 DiskANN 索引拆分为更小、更易于管理的部分来帮助优化大规模矢量搜索。 通过在容器的索引策略中指定 VectorIndexShardKey,可以为所选文档属性的每个唯一值创建一个 DiskANN 索引。

此方法可以提高查询性能、提高召回率和降低 RU 成本,尤其是在处理高基数数据时。 无论是构建建议引擎、语义搜索还是智能代理,分片 DiskANN 都能够更好地控制矢量索引的结构和执行方式。

在这里,我们可以看到一个基于 tenantID 属性定义分片键的示例。 这可以是数据项的任何属性,甚至是分区键。 单个字符串需要括在数组中。 若要了解详细信息,请参阅 分片 DiskANN:重点矢量搜索

"vectorIndexes": [
    {
        "path": "/vector2",
        "type": "DiskANN",
        "vectorIndexShardKey": ["/tenantID"]
    }
]

元组索引策略示例

此示例索引策略定义元组索引和 events.nameevents.category.

{  
    "automatic":true,
    "indexingMode":"Consistent",
    "includedPaths":[  
        {"path":"/*"}, 
        {"path":"/events/[]/{name,category}/?"} 
    ],
    "excludedPaths":[],
    "compositeIndexes":[]
}

上述索引用于以下查询。

SELECT * 
FROM root r 
WHERE 
   EXISTS (SELECT VALUE 1 FROM ev IN r.events 
           WHERE ev.name = 'M&M' AND ev.category = 'Candy') 

组合索引策略示例

除了包含或排除各属性的路径,还可以指定一个组合索引。 若要执行具有针对多个属性的 ORDER BY 子句的查询,需要这些属性上的组合索引。 如果查询包含筛选器以及对多个属性进行排序,则可能需要多个复合索引。

对于具有多个筛选器或同时具有筛选器和 ORDER BY 子句的查询,组合索引也具有性能优势。

注意

组合路径具有隐式 /?,因为该路径上只有标量值被索引。 /*复合路径不支持通配符。 不应在组合路径中指定 /?/*。 复合路径也区分大小写。

针对(name asc、age desc)定义的组合索引

{  
    "automatic":true,
    "indexingMode":"Consistent",
    "includedPaths":[  
        {  
            "path":"/*"
        }
    ],
    "excludedPaths":[],
    "compositeIndexes":[  
        [  
            {  
                "path":"/name",
                "order":"ascending"
            },
            {  
                "path":"/age",
                "order":"descending"
            }
        ]
    ]
}

以下查询需要姓名和年龄的复合索引:

查询 #1:

SELECT *
FROM c
ORDER BY c.name ASC, c.age DESC

查询 #2:

SELECT *
FROM c
ORDER BY c.name DESC, c.age ASC

此复合索引有利于以下查询并优化筛选器:

查询 #3:

SELECT *
FROM c
WHERE c.name = "Tim"
ORDER BY c.name DESC, c.age ASC

查询 #4:

SELECT *
FROM c
WHERE c.name = "Tim" AND c.age > 18

针对 (name ASC, age ASC) 和 (name ASC, age DESC) 定义的组合索引

可以在同一个索引策略中定义多个组合索引。

{  
    "automatic":true,
    "indexingMode":"Consistent",
    "includedPaths":[  
        {  
            "path":"/*"
        }
    ],
    "excludedPaths":[],
    "compositeIndexes":[  
        [  
            {  
                "path":"/name",
                "order":"ascending"
            },
            {  
                "path":"/age",
                "order":"ascending"
            }
        ],
        [  
            {  
                "path":"/name",
                "order":"ascending"
            },
            {  
                "path":"/age",
                "order":"descending"
            }
        ]
    ]
}

针对 (name ASC, age ASC) 定义的组合索引

可以选择指定顺序。 如果未指定,顺序为升序。

{  
    "automatic":true,
    "indexingMode":"Consistent",
    "includedPaths":[  
        {  
            "path":"/*"
        }
    ],
    "excludedPaths":[],
    "compositeIndexes":[  
        [  
            {  
               "path":"/name"
            },
            {  
               "path":"/age"
            }
        ]
    ]
}

排除所有属性路径,但保持索引编制活动状态

可以使用此策略,其中 生存时间(TTL) 功能处于活动状态,但不需要其他索引将 Azure Cosmos DB 用作纯键值存储。

{
    "indexingMode": "consistent",
    "includedPaths": [],
    "excludedPaths": [{
        "path": "/*"
    }]
}

无索引

此策略会关闭索引。 如果 indexingMode 设为 none,则无法在容器上设置 TTL。

{
    "indexingMode": "none"
}

更新索引策略

在 Azure Cosmos DB 中,可以使用以下任一方法更新索引策略:

  • 通过 Azure 门户
  • 使用 Azure CLI
  • 使用 PowerShell
  • 使用某个 SDK

索引策略更新会触发索引转换。 还可以使用 SDK 跟踪此转换的进度。

注意

更新索引策略时,对 Azure Cosmos DB 的写入不会中断。 详细了解索引转换

重要

删除索引会立即生效,而添加新索引需要一些时间,因为它需要索引转换。 将一个索引替换为另一个索引(例如,将单个属性索引替换为复合索引),请确保先添加新索引,然后等待索引转换完成, 然后再 从索引策略中删除以前的索引。 否则,这会对查询上一个索引的能力产生负面影响,并可能会中断引用上一个索引的任何活动工作负荷。

使用 Azure 门户

Azure Cosmos DB 容器将其索引策略存储为 JSON 文档,可以在 Azure 门户中直接编辑这些文档。

  1. 登录 Azure 门户

  2. 创建新的 Azure Cosmos DB 帐户或选择现有的帐户。

  3. 打开“数据资源管理器”窗格,选择要使用的容器。

  4. 选择 “设置”,然后选择 “索引策略”。

  5. 修改索引策略 JSON 文档,如这些例子所示。

  6. 完成时选择“保存” 。

显示如何使用 Azure 门户管理索引的屏幕截图。

使用 Azure CLI

若要创建具有自定义索引策略的容器,请参阅使用 CLI 创建具有自定义索引策略的容器

使用 PowerShell

若要创建具有自定义索引策略的容器,请参阅使用 PowerShell 创建具有自定义索引策略的容器

使用 .NET SDK

ContainerProperties 中的 对象公开了一个 IndexingPolicy 属性,可以通过该属性更改 IndexingMode 以及添加或删除 IncludedPathsExcludedPaths。 有关详细信息,请参阅 快速入门:将 Azure Cosmos DB for NoSQL 与用于 .NET 的 Azure SDK 配合使用

// Retrieve the container's details
ContainerResponse containerResponse = await client.GetContainer("database", "container").ReadContainerAsync();
// Set the indexing mode to consistent
containerResponse.Resource.IndexingPolicy.IndexingMode = IndexingMode.Consistent;
// Add an included path
containerResponse.Resource.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" });
// Add an excluded path
containerResponse.Resource.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/name/*" });
// Add a spatial index
SpatialPath spatialPath = new SpatialPath
{
    Path = "/locations/*"
};
spatialPath.SpatialTypes.Add(SpatialType.Point);
containerResponse.Resource.IndexingPolicy.SpatialIndexes.Add(spatialPath);
// Add a composite index
containerResponse.Resource.IndexingPolicy.CompositeIndexes.Add(new Collection<CompositePath> { new CompositePath() { Path = "/name", Order = CompositePathSortOrder.Ascending }, new CompositePath() { Path = "/age", Order = CompositePathSortOrder.Descending } });
// Update container with changes
await client.GetContainer("database", "container").ReplaceContainerAsync(containerResponse.Resource);

若要跟踪索引转换进度,请传递一个 RequestOptions 对象,用以将 PopulateQuotaInfo 属性设置为 true。 从 x-ms-documentdb-collection-index-transformation-progress 响应头中检索值。

// retrieve the container's details
ContainerResponse containerResponse = await client.GetContainer("database", "container").ReadContainerAsync(new ContainerRequestOptions { PopulateQuotaInfo = true });
// retrieve the index transformation progress from the result
long indexTransformationProgress = long.Parse(containerResponse.Headers["x-ms-documentdb-collection-index-transformation-progress"]);

在创建新容器的同时定义自定义索引策略时,SDK V3 Fluent API 可让你以简洁高效的方式编写这个定义:

await client.GetDatabase("database").DefineContainer(name: "container", partitionKeyPath: "/myPartitionKey")
    .WithIndexingPolicy()
        .WithIncludedPaths()
            .Path("/*")
        .Attach()
        .WithExcludedPaths()
            .Path("/name/*")
        .Attach()
        .WithSpatialIndex()
            .Path("/locations/*", SpatialType.Point)
        .Attach()
        .WithCompositeIndex()
            .Path("/name", CompositePathSortOrder.Ascending)
            .Path("/age", CompositePathSortOrder.Descending)
        .Attach()
    .Attach()
    .CreateIfNotExistsAsync();

使用 Java SDK

DocumentCollection 中的 对象公开 getIndexingPolicy()setIndexingPolicy() 方法。 通过它们操作的 IndexingPolicy 对象,你可以更改索引模式,以及添加或删除包括的和排除的路径。 有关详细信息,请参阅 快速入门:将 Azure Cosmos DB for NoSQL 与用于 Java 的 Azure SDK 配合使用

// Retrieve the container's details
Observable<ResourceResponse<DocumentCollection>> containerResponse = client.readCollection(String.format("/dbs/%s/colls/%s", "database", "container"), null);
containerResponse.subscribe(result -> {
DocumentCollection container = result.getResource();
IndexingPolicy indexingPolicy = container.getIndexingPolicy();

// Set the indexing mode to consistent
indexingPolicy.setIndexingMode(IndexingMode.Consistent);

// Add an included path

Collection<IncludedPath> includedPaths = new ArrayList<>();
IncludedPath includedPath = new IncludedPath();
includedPath.setPath("/*");
includedPaths.add(includedPath);
indexingPolicy.setIncludedPaths(includedPaths);

// Add an excluded path

Collection<ExcludedPath> excludedPaths = new ArrayList<>();
ExcludedPath excludedPath = new ExcludedPath();
excludedPath.setPath("/name/*");
excludedPaths.add(excludedPath);
indexingPolicy.setExcludedPaths(excludedPaths);

// Add a spatial index

Collection<SpatialSpec> spatialIndexes = new ArrayList<SpatialSpec>();
Collection<SpatialType> collectionOfSpatialTypes = new ArrayList<SpatialType>();

SpatialSpec spec = new SpatialSpec();
spec.setPath("/locations/*");
collectionOfSpatialTypes.add(SpatialType.Point);
spec.setSpatialTypes(collectionOfSpatialTypes);
spatialIndexes.add(spec);

indexingPolicy.setSpatialIndexes(spatialIndexes);

// Add a composite index

Collection<ArrayList<CompositePath>> compositeIndexes = new ArrayList<>();
ArrayList<CompositePath> compositePaths = new ArrayList<>();

CompositePath nameCompositePath = new CompositePath();
nameCompositePath.setPath("/name");
nameCompositePath.setOrder(CompositePathSortOrder.Ascending);

CompositePath ageCompositePath = new CompositePath();
ageCompositePath.setPath("/age");
ageCompositePath.setOrder(CompositePathSortOrder.Descending);

compositePaths.add(ageCompositePath);
compositePaths.add(nameCompositePath);

compositeIndexes.add(compositePaths);
indexingPolicy.setCompositeIndexes(compositeIndexes);

// Update the container with changes

 client.replaceCollection(container, null);
});

若要在容器上跟踪索引转换进度,请传递一个用以请求要填充的配额信息的 RequestOptions 对象。 从 x-ms-documentdb-collection-index-transformation-progress 响应头中检索值。

// set the RequestOptions object
RequestOptions requestOptions = new RequestOptions();
requestOptions.setPopulateQuotaInfo(true);
// retrieve the container's details
Observable<ResourceResponse<DocumentCollection>> containerResponse = client.readCollection(String.format("/dbs/%s/colls/%s", "database", "container"), requestOptions);
containerResponse.subscribe(result -> {
    // retrieve the index transformation progress from the response headers
    String indexTransformationProgress = result.getResponseHeaders().get("x-ms-documentdb-collection-index-transformation-progress");
});

使用 Node.js SDK

ContainerDefinition 中的 接口公开 indexingPolicy 属性,可以通过该属性更改 indexingMode 并添加或删除 includedPathsexcludedPaths。 有关详细信息,请参阅 快速入门:将 Azure Cosmos DB for NoSQL 与用于 Node.js的 Azure SDK 配合使用

检索容器的详细信息:

const containerResponse = await client.database('database').container('container').read();

将索引模式设置为“一致”:

containerResponse.body.indexingPolicy.indexingMode = "consistent";

添加包含的路径(包括空间索引):

containerResponse.body.indexingPolicy.includedPaths.push({
    includedPaths: [
      {
        path: "/age/*",
        indexes: [
          {
            kind: cosmos.DocumentBase.IndexKind.Range,
            dataType: cosmos.DocumentBase.DataType.String
          },
          {
            kind: cosmos.DocumentBase.IndexKind.Range,
            dataType: cosmos.DocumentBase.DataType.Number
          }
        ]
      },
      {
        path: "/locations/*",
        indexes: [
          {
            kind: cosmos.DocumentBase.IndexKind.Spatial,
            dataType: cosmos.DocumentBase.DataType.Point
          }
        ]
      }
    ]
  });

添加排除的路径:

containerResponse.body.indexingPolicy.excludedPaths.push({ path: '/name/*' });

将更改更新到容器:

const replaceResponse = await client.database('database').container('container').replace(containerResponse.body);

若要跟踪容器上的索引转换进度,请传递一个将 RequestOptions 属性设为 populateQuotaInfotrue 对象。 从 x-ms-documentdb-collection-index-transformation-progress 响应头中检索值。

// retrieve the container's details
const containerResponse = await client.database('database').container('container').read({
    populateQuotaInfo: true
});
// retrieve the index transformation progress from the response headers
const indexTransformationProgress = replaceResponse.headers['x-ms-documentdb-collection-index-transformation-progress'];

添加组合索引:

 console.log("create container with composite indexes");
  const containerDefWithCompositeIndexes = {
    id: "containerWithCompositeIndexingPolicy",
    indexingPolicy: {
      automatic: true,
      indexingMode: IndexingMode.consistent,
      includedPaths: [
        {
          path: "/*",
        },
      ],
      excludedPaths: [
        {
          path: '/"systemMetadata"/*',
        },
      ],
      compositeIndexes: [
        [
          { path: "/field", order: "ascending" },
          { path: "/key", order: "ascending" },
        ],
      ],
    },
  };
  const containerWithCompositeIndexes = (
    await database.containers.create(containerDefWithCompositeIndexes)
  ).container;

使用 Go SDK

IndexingPolicy 结构定义容器的索引策略。 创建新容器或重新配置现有容器时,可以使用它。

db, _ := client.NewDatabase("demodb")

pkDefinition := azcosmos.PartitionKeyDefinition{
	Paths: []string{"/state"},
		Kind:  azcosmos.PartitionKeyKindHash,
}

indexingPolicy := &azcosmos.IndexingPolicy{
	IndexingMode: azcosmos.IndexingModeConsistent,

    // add an included path
	IncludedPaths: []azcosmos.IncludedPath{
		{Path: "/*"},
	},

    // add an excluded path
	ExcludedPaths: []azcosmos.ExcludedPath{
		{Path: "/address/*"},
	},

    // add composite indices
	CompositeIndexes: [][]azcosmos.CompositeIndex{
		{
			{
				Path:  "/name",
				Order: azcosmos.CompositeIndexAscending,
			},
			{
				Path:  "/age",
				Order: azcosmos.CompositeIndexDescending,
			},
		},
	}

	db.CreateContainer(context.Background(), azcosmos.ContainerProperties{
		ID:                     "demo_container",
		PartitionKeyDefinition: pkDefinition,
		IndexingPolicy:         indexingPolicy,
	}, nil)

使用 Python SDK

使用 Python SDK V3 时,容器配置作为字典进行管理。 从此字典中,可以访问索引策略及其所有属性。 有关详细信息,请参阅 快速入门:将 Azure Cosmos DB for NoSQL 与用于 Python 的 Azure SDK 配合使用

检索容器的详细信息:

containerPath = 'dbs/database/colls/collection'
container = client.ReadContainer(containerPath)

将索引模式设置为“一致”:

container['indexingPolicy']['indexingMode'] = 'consistent'

使用包含的路径和空间索引定义索引策略:

container["indexingPolicy"] = {

    "indexingMode":"consistent",
    "spatialIndexes":[
                {"path":"/location/*","types":["Point"]}
             ],
    "includedPaths":[{"path":"/age/*","indexes":[]}],
    "excludedPaths":[{"path":"/*"}]
}

使用排除的路径定义索引策略:

container["indexingPolicy"] = {
    "indexingMode":"consistent",
    "includedPaths":[{"path":"/*","indexes":[]}],
    "excludedPaths":[{"path":"/name/*"}]
}

添加组合索引:

container['indexingPolicy']['compositeIndexes'] = [
                [
                    {
                        "path": "/name",
                        "order": "ascending"
                    },
                    {
                        "path": "/age",
                        "order": "descending"
                    }
                ]
                ]

将更改更新到容器:

response = client.ReplaceContainer(containerPath, container)