【问题标题】:Most efficient way to query across partition with CosmosDB for bulk operations使用 CosmosDB 跨分区查询以进行批量操作的最有效方法
【发布时间】:2025-12-19 03:00:06
【问题描述】:

我有一个跨分区查询,它依次返回每个分区的行,这是有道理的,所有分区 1 的结果,所有分区 2 的结果等等。

对于返回的每一行,我需要执行一个操作,可以是删除或更新。

记录太多,无法全部读取并执行操作,因此我需要将结果流式传输并同时执行操作。

我遇到的问题是我很快就用完了 RU,因为我的操作依次在每个分区上运行,并且单个分区分配了十分之一的 RU。

我可以在FeedOptions 中指定PartitionKey,但这对我没有帮助,因为我不知道密钥是什么。

我的查询看起来像

select r.* from r where r.deleted 

partition 位于名为 container 的字段上

假设我有以下物品

container|title    |deleted
jamjar   |jam      |true      <--- stored in partition 5
jar      |pickles  |true      <--- stored in partition 5
tin      |cookies  |true      <--- stored in partition 8
tub      |sweets   |true      <--- stored in partition 9

我愿意select r.title from r where r.deleted 我的查询将按以下顺序返回行

jam      <--- stored in partition 5
pickles  <--- stored in partition 5
cookies  <--- stored in partition 8
sweets   <--- stored in partition 9

我使用ActionBlock 允许我启动 2 个线程来对返回的每一行执行我的操作,所以我处理 jampickles 然后 cookiessweets 从而消耗分区中的 RU 5 当我对jampickles 执行操作时

我希望结果返回为:

jam      <--- stored in partition 5
cookies  <--- stored in partition 8
sweets   <--- stored in partition 9
pickles  <--- stored in partition 5

对于正常的 API 调用,我们总是知道 container,这是批量且非常不频繁的删除的要求。

如果知道分区号并且可以将分区号提供给查询就可以了,我很乐意发出 10 个查询并将其视为 10 个单独的作业。

【问题讨论】:

  • 您可以运行查询SELECT DISTINCT c.container FROM c where c.deleted = true 选择所有已删除项目的分区,然后运行存储过程为每个分区批量删除文档
  • 我的示例可能具有误导性,我可以读取所有数据,问题是,它按顺序使用每个分区上的所有 RU,而不是在初始选择中为每个分区返回几行。跨度>
  • 我猜,是按照cosmos里面的分区设计的。因此,具有相同分区键范围的分区的每个文档都位于相同的物理位置。这就是为什么当使用查询跨分区时,您将首先从一个物理位置看到结果,然后是其他位置。为了解决您的问题,您可以编写自己的管理器,将负载分配到客户端的分区上。
  • cosmos 只是自己处理分区,这对于正常使用来说很好,它只是我的一次批量操作受阻。我刚刚重新阅读了其中一个文档,我想我需要这样做docs.microsoft.com/en-us/azure/cosmos-db/…(我确定我之前尝试过:))
  • 我设置了 EnableCrossPartitionQuery 选项并使用 fiddler 来查看会发生什么,我可以看到它发出 10 个请求并设置一个看起来与分区有关的标头——我已经回答了下面

标签: azure-cosmosdb


【解决方案1】:

您需要设置MaxDegreeOfParallelism,它是FeedOptions 的一部分:

FeedOptions queryOptions = new FeedOptions
{
   EnableCrossPartitionQuery = true,
   MaxDegreeOfParallelism = 10,
};

它将为每个分区创建一个客户端线程,如果您检查 HTTP 标头,您可以看到发生了什么

x-ms-documentdb-query-enablecrosspartition: True
x-ms-documentdb-query-parallelizecrosspartitionquery: True
x-ms-documentdb-populatequerymetrics: False
x-ms-documentdb-partitionkeyrangeid: QQlvANNcKgA=,3

注意 QQlvANNcKgA=,3 你看到其中的 10 个,从 ,0,9 我怀疑第一部分是某个页面跟踪,第二部分是分区

请参阅文档Parallel query execution

这是 Fiddler 中 3 个查询的时间线视图:

  • MaxDegreeOfParallelism = 10:较慢且不太并行,而线程和连接正在旋转(您可以在左侧列表中看到 5 次额外的 SSL 握手,并且在时间线)。还有 2 个(出于某种原因)请求获取集合的 PK 范围
  • MaxDegreeOfParallelism = 10(再次):几乎是最佳并行。 PK 范围信息似乎是从上一个请求中缓存的,并在此处重复使用,而不会发出任何无关请求。
  • MaxDegreeOfParallelism = 0:完全顺序。
    有趣的是,这些请求没有指定 x-ms-documentdb-partitionkeyrangeid 标头。

使用 DocumentClient v2.x 针对具有 6 个物理分区的集合运行查询。

另请注意,每个查询都会触发 7 个请求,第一个是“查询计划请求”(不可并行化),而以下 6 个返回实际数据。

【讨论】:

  • 根据经验,第一部分(看起来像 Base64 字符串)是 collection 的内部 id(又名_rid)。对于针对该集合的所有查询,它应该始终相同。
  • 我希望您不介意我编辑您的答案(如果您愿意,请随时回复)。没有简单的方法在评论中嵌入图片,我不想在没有足够背景的情况下单独发布答案,也不想复制您的内容。
最近更新 更多