Cosmos 分区键上的 STARTSWITH 是否优化了跨分区查询"fan-out"?



Microsoft明确表示,跨分区查询"扇出";对每个分区(链接(的查询:

以下查询在分区键(DeviceId(上没有筛选器。因此,它必须扇出到所有物理分区,在这些分区中,它针对每个分区的索引运行:

所以我很好奇,如果;扇出";可以通过对分区键(如STARTSWITH(执行范围查询来优化。

为了测试它,我创建了一个包含七个文档的小型Cosmos DB:

{
"partitionKey": "prefix1:",
"id": "item1a"
},
{
"partitionKey": "prefix1:",
"id": "item1b"
},
{
"partitionKey": "prefix1:",
"id": "item1c"
},
{
"partitionKey": "prefix1X:",
"id": "item1d"
},
{
"partitionKey": "prefix2:",
"id": "item2a"
},
{
"partitionKey": "prefix2:",
"id": "item2b"
},
{
"partitionKey": "prefix3:",
"id": "item3a"
}

它具有带有分区键"的默认索引策略/partitionKey";。然后我运行了一堆查询:

SELECT * FROM c WHERE STARTSWITH(c.partitionKey, 'prefix1')
-- Actual Request Charge: 2.92 RUs
SELECT * FROM c WHERE c.partitionKey = 'prefix1:' OR c.partitionKey = 'prefix1X:'
-- Actual Request Charge: 3.02 RUs
SELECT * FROM c WHERE STARTSWITH(c.partitionKey, 'prefix1:')
SELECT * FROM c WHERE c.partitionKey = 'prefix1:'
-- Each Query Has Actual Request Charge: 2.89 RUs
SELECT * FROM c WHERE STARTSWITH(c.partitionKey, 'prefix2')
SELECT * FROM c WHERE c.partitionKey = 'prefix2:'
-- Each Query Has Actual Request Charge: 2.86 RUs
SELECT * FROM c WHERE STARTSWITH(c.partitionKey, 'prefix3')
SELECT * FROM c WHERE c.partitionKey = 'prefix3:'
-- Each Query Has Actual Request Charge: 2.83 RUs
SELECT * FROM c WHERE c.partitionKey = 'prefix2:' OR c.partitionKey = 'prefix3:'
-- Actual Request Charge: 2.99 RUs

重新运行查询时,请求费用是一致的。费用增长的模式似乎与结果集和查询复杂性一致,可能"OR"查询除外。然而,后来我尝试了这个:

SELECT * FROM c
-- Actual Request Charge: 2.35 RUs

而且,即使使用相等运算符,对所有分区的基本扇出也比针对特定分区更快。我不明白怎么会这样。

尽管如此,我的样本数据库非常小,只有七个文档。查询集可能不够大,无法信任结果。

那么,如果我有数百万个文档,STARTSWITH(c.partitionKey,"prefix"(会比分散到所有分区更优化吗?

我自己试图确定这种方法是否有任何好处,根据答案,似乎没有。

我刚刚了解了新的分层分区密钥功能,它在私人预览中,似乎可以解决我们试图解决的问题:

https://devblogs.microsoft.com/cosmosdb/hierarchical-partition-keys-private-preview/

分层分区密钥现在可以在私有预览中用于Azure Cosmos DB核心(SQL(API。利用分层分区密钥,也称为子分区,您现在可以对容器,最多包含三个级别的分区键。这使得多租户场景的最佳分区策略否则将使用合成分区键的工作负载。相反必须选择一个分区键,这通常会导致性能权衡–您现在最多可以使用三个键来进一步对数据进行分区,实现更优化的数据分发和更高的规模。

由于这最多允许3个键,因此可以通过将前缀分解为单独的键来解决问题,或者如果前缀超过3个,则至少可以进一步优化前缀。

示例(链接中的用法示例(:https://github.com/AzureCosmosDB/HierarchicalPartitionKeysFeedbackGroup#net-v3-sdk-2

// Get the full partition key path
var id = "0a70accf-ec5d-4c2b-99a7-af6e2ea33d3d"; 
var fullPartitionkeyPath = new PartitionKeyBuilder()
.Add("Contoso") //TenantId
.Add("Alice") //UserId
.Build();
var itemResponse = await containerSubpartitionByTenantId_UserId.ReadItemAsync<dynamic>(id, fullPartitionkeyPath);

注意事项

从预览链接来看,你需要选择进入预览并创建一个新的容器

仅限新容器–必须在容器上指定所有密钥创建

随着规模的扩大,您得到的"逻辑分区";每个";物理分区";,直到最终每个分区键值都有自己的物理分区。

因此:

如果我有数百万个文档,STARTSWITH(c.partitionKey,'prefix'(会比分散到所有分区更优化吗?

这两个查询都将在多个分区中展开。

我非常确信;Azure Cosmos DB使用基于散列的分区来将逻辑分区分布在物理分区上";,具有公共前缀的分区键之间没有位置性,并且每个STARTSWITH查询都必须在所有物理分区中展开。

文档表明存在一些效率

使用Azure Cosmos DB,查询通常按以下顺序执行,从最快/最高效到较慢/效率较低。

  • 获取单个分区键和项键
  • 在单个分区键上使用filter子句进行查询
  • 在任何属性上都没有相等或范围筛选子句的查询
  • 不带筛选器的查询

最新更新