【问题标题】:Cosmos DB partition key and query design for sequential access用于顺序访问的 Cosmos DB 分区键和查询设计
【发布时间】:2020-09-19 04:17:37
【问题描述】:

我们希望在 Cosmos DB 中存储一组文档,其主键为 EventId。这些记录平均分布在多个客户中。随着新文档的添加,客户需要访问一部分客户的最新记录。文档是不可变的,需要无限期地存储。

我们应该如何设计我们的分区键和查询来避免客户端都碰到相同的分区和/或高 RU 使用率?

如果我们只使用CustomerId作为分区键,我们最终会超出逻辑分区的10GB限制,如果我们使用EventId,那么查询就会变得低效(会导致在跨分区查询中,以及我们希望避免的高 RU 使用率)。

另一个想法是将文档分组到块中。即 PartitionKey = int(EventId / PartitionSize)。这将导致所有客户端访问最新的分区,这可能会导致性能下降和节流。

如果我们使用 CustomerIdint(EventId / PartitionSize) 的组合 PartitionKey,那么我不清楚如何避免跨分区查询来检索正确的文档集。

编辑:

澄清几点:

  • 客户端将通过指定CustomerId 的列表、他们收到的最后一个EventId 以及要检索的最大记录数来访问事件。
  • 因此,单独使用EventId 效果不佳,因为它会导致跨分区查询(即WHERE EventId > LastEventId)。
  • 系统可能会以 15 分钟为增量每天写入大约 1GB。
  • 很难知道读取量会是多少,但我猜可能适中,可能有几千个客户端定期轮询 API。

【问题讨论】:

  • 通常你不能优化读写的分区。要么让它有利于读取,要么让它有利于写入。实现这两者的常用方法是通过更改源将集合复制到具有不同分区方案的另一个集合(甚至是不同类型的数据库)。
  • 这将有助于提供有关您希望运行的最常见或性能关键查询和预期数量的一些详细信息。例如,如果您期望 80/20 读/写与 20/80 相比,最佳答案可能会有所不同。
  • @NoahStahl 我已经添加了一些关于预期数量的更多细节。

标签: azure azure-cosmosdb


【解决方案1】:

首先,逻辑分区大小限制现在已增加到 20GB,请参阅here

您也可以将 EventID 用作分区,因为您有逻辑分区大小的限制(以 GB 为单位),但您对逻辑分区的数量没有限制。所以使用 EventID 很好,如果您使用 EventID 查询,您将获得非常快的点对点读取。现在您提到使用这种方式您将不得不进行跨分区查询,您能解释一下吗?

需要记住的一点是,Cosmos DB 并不是真正用于存储这种基于日志的数据,因为它将所有内容都存储在 SSD 中,因此请计算一下您的 1 个文档大小以及您在一秒钟内会拥有多少个文档然后存储一天多少到一个月多少。您可以在完成后使用 TTL 从 Cosmos 中删除,并将其长期存储在 Azure BLOB 存储中,为了快速检索,请使用 Azure Search 在搜索查询中使用 CustomerID 和 EventID 来查询 BLOB 中的数据。

【讨论】:

  • 我已对问题进行了澄清。本质上,客户将询问所有最近的事件,而不是一次一个。关于使用长期存储的真正好处 - 我会记住这一点,谢谢。
  • 如果这只是附加数据,那么我建议使用 Azure 数据资源管理器。它速度超快,在我看来长期比 Cosmos DB 便宜。但即使使用 EventID 作为分区键,您也可以使用 Azure 搜索来索引您的 Cosmos DB 容器,并且使用起来非常简单。在 Azure 搜索中构建一个查询,您将通过带有客户 ID 的 REST 端点调用该查询,您可以从中按前 10 个最近的事件进行排序。但如果这是仅附加数据,那么使用 Cosmos 甚至 SQL 没有意义,Azure 数据资源管理器更有意义。
【解决方案2】:

我们应该如何设计我们的分区键和查询,以避免所有客户端都访问相同的分区和/或高 RU 使用率?

前段时间我遇到了类似的问题,并且 PartitionKey 带有customerId + datekey,例如cust1_20200920 对我来说效果很好。

我将日期键创建为20200920 (YYYYMMDD),但您可以根据您的查询要求选择忽略日期部分甚至月份(cust1_202009 /cust1_2020)

另外,IMO,如果在查询时有多个已知的 PartitionKey,这是一件好事。比如你保留YYYYMM作为PartitionKey,想获取4个月的数据,可以并行运行4个查询,合并数据。如果您有很多客户端,并且这些分区键分布在多个物理分区中,这会更快。

另外,Cosmos Db 最近为交易数据引入了一个分析存储,这对您的用例很有用。 更多信息在这里 - https://docs.microsoft.com/en-us/azure/cosmos-db/analytical-store-introduction

【讨论】:

  • 我们在使用这种方法时遇到的主要问题是:您如何确定客户缺少的时间块是否对应于没有事件,因为客户端是最新的,或者没有事件,因为在那段时间里没有客户的事件?我想,最简单的方法是添加某种空分区标记,但是当新客户添加到系统中时,您需要维护这些标记。
  • 你是如何生成 EventId 的?如果您想使用 WHERE EventId > LastEventId 之类的查询,则事件 id 必须是数字。在这种情况下,您可以将 eventId 与适当的因子分开,并使用它来创建分区键。
【解决方案3】:

一种方法是使用多个 Cosmos 容器作为具有不同分区的“热/冷”层。我们可以使用两个容器:

  • Recent:对最近项目的所有写入和所有查询都在这里。由CustomerId 分区。
  • Archive:所有项目都复制到这里,以便长期存储和访问。按CustomerId + 时间跨度分区(例如每个日历月的分区)

Recent 容器将按客户提供单分区查询。每个分区的数据增长将通过在创建期间设置合理的TTL 来限制,或者在项目不再是最近项目查询的候选对象时使用单独的维护作业(可能是 Azure Function on timer)来删除它们。

Change Feed 处理器(由 Azure 函数或其他方式实现)将在 Recent 中的每个创建时触发并复制到 Archive。此副本将具有结合客户 ID 和日期范围的分区键,以限制分区大小。

此方案应提供来自Recent 的有效最近项目查询和Archive 中的安全长期存储,在给定所需日期范围的情况下具有合理的Archive 查询效率。主要缺点是每个项目两次写入(每个容器一次)——但这是有效轮询的权衡。这种权衡是否值得最好通过模拟负载和观察性能来确定。

【讨论】:

    猜你喜欢
    • 2022-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多