【问题标题】:Does STARTSWITH on Cosmos partition keys optimize "fan-out" of cross-partition queries?Cosmos 分区键上的 STARTSWITH 是否优化了跨分区查询的“扇出”?
【发布时间】:2020-12-31 22:34:30
【问题描述】:

微软明确表示跨分区查询将查询“扇出”到每个分区 (link):

以下查询对分区键 (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') 会比散播到所有分区更优化吗?

【问题讨论】:

    标签: azure-cosmosdb azure-cosmosdb-sqlapi


    【解决方案1】:

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

    我刚刚了解了私人预览版中的新分层分区键功能,似乎解决了我们正在尝试解决的问题:

    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);
    

    注意事项

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

    仅限新容器 - 必须在容器上指定所有键 创作

    【讨论】:

      【解决方案2】:

      随着您的扩展,每个“physical partition”获得的“logical partitions”越来越少,直到最终每个分区键值都有自己的物理分区。

      所以:

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

      两个查询都将跨多个分区扇出。

      而且我很确定,由于“Azure Cosmos DB 使用基于哈希的分区在物理分区之间传播逻辑分区”,具有公共前缀的分区键之间没有局部性,并且每个 STARTSWITH 查询都必须扇出跨所有物理分区。

      【讨论】:

      • 我了解 STARTSWITH 可能会扫描“多个”分区。所以改写我的问题 - 使用 STARTSWITH 会阻止扫描与 STARTSWITH 过滤器不匹配的分区吗?我希望扫描只发生在与过滤器匹配的分区上,而不是扫描与过滤器不匹配的分区。
      • 不,为什么会这样?它是怎么知道的?
      • @4c74356b41 我不确定我是否理解这个问题。基本上,Cosmos 服务必须查看分区键上存在条件的查询,就像使用相等运算符一样,并将其解析出来以得出查询的其余部分适用的分区集。或者对于 Cosmos 来说,除了等式之外的任何东西都比较复杂,相反,它只是扇出,每个分区都会扫描所有文档,即使查询甚至不适用于它。
      • 我认为你的回答是正确的,我最初的问题来自一个关于“扇形”的错误前提,因为我认为它正在扇形到逻辑分区(因为它们是由分区键标识的),但是扇形是物理路由。整个分区键(不是 STARTSWITH 的部分键)将允许 Cosmos 以逻辑分区的物理集为目标以在其上运行查询,但由于我使用的是 STARTSWITH,它无法确定这一点,并且必须扇出所有物理集的本地分区。我的问题示例只有一个物理分区,所以这是一个不好的示例。
      • 我开始明白单个物理分区上的所有逻辑分区都使用相同的“索引”,因此它的物理路由很重要。我问的原因是我试图滥用分区键给我多个数据命名空间,但每个命名空间数据分布在多个分区中以提高性能。比如doc-type-A有100个逻辑分区,doc-type-B有50个逻辑分区,那么查询的时候我只需要定位到包含特定doc-type的分区。文档不需要在同一个逻辑分区中。
      【解决方案3】:

      docs 表示有一些效率

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多