我们希望使用主键EventId
将一组文档存储在Cosmos DB中。这些记录平均分布在许多客户中。添加新文档时,客户需要访问一部分客户的最新记录。这些文档是不可变的,需要无限期地存储。
我们应该如何设计分区键和查询,以避免客户端全部访问相同的分区和/或较高的RU使用率?
如果仅使用CustomerId
作为分区键,则逻辑分区最终将超出 10GB 限制,如果使用EventId
,则查询变为效率低下(将导致跨分区查询和较高的RU使用率,我们希望避免这种情况)。
另一个想法是将文档分组。即PartitionKey = int(EventId / PartitionSize)
。这将导致所有客户端都使用最新的分区,这可能会导致性能下降和节流。
如果我们使用CustomerId
和int(EventId / PartitionSize)
的组合PartitionKey,那么我不清楚如何避免跨分区查询来检索正确的文档集。
编辑:
澄清两点:
CustomerId
的列表,收到的最后EventId
以及要检索的最大记录数来访问事件。EventId
的效果不好,因为它将导致跨分区查询(即WHERE EventId > LastEventId
)。答案 0 :(得分:0)
首先,逻辑分区的大小限制现在已增加到20GB,请参阅here。
您还可以将EventID用作分区,因为逻辑分区的大小以GB为单位有限制,但是逻辑分区的数量没有限制。因此,使用EventID很好,如果使用EventID进行查询,则可以快速读取点对点。现在您提到使用这种方式,您将不得不进行跨分区查询,您能解释一下吗?
请记住,Cosmos DB并不是真正用于存储此类基于Log的数据,因为它会将所有内容存储在SSD中,因此请计算一下您的1个文档的大小以及一秒钟的大小然后存储一天中的多少到一个月中的多少。完成后,您可以使用TTL从Cosmos中删除,并将其长期存储在Azure BLOB存储中;为了进行快速检索,请使用Azure Search在搜索查询中使用CustomerID和EventID在BLOB中查询数据。>
答案 1 :(得分:0)
我们应该如何设计分区键和查询,以避免客户端都访问相同的分区和/或较高的RU使用率?
前段时间,我遇到了类似的问题,并且遇到了带有customerId + datekey
的PartitionKey,例如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
答案 2 :(得分:0)
一种方法是将多个Cosmos容器用作具有不同分区的“热/冷”层。我们可以使用两个容器:
Recent
:所有写入和对最近项目的所有查询都在这里。由CustomerId
分区。Archive
:所有项目都复制到此处以进行长期存储和访问。按CustomerId
+时间间隔进行分区(例如,每个日历月的分区) Recent
容器将按客户提供单个分区查询。通过在创建过程中设置合理的TTL来限制每个分区的数据增长,或者在不再将其用作最近项查询的候选项时使用单独的维护作业(可能是计时器上的Azure函数)来删除它们。
由Azure函数或其他方式实现的Change Feed处理器将在Recent
中的每次创建时触发并复制到Archive
中。此副本将具有适当组合客户ID和日期范围的分区键,以限制分区大小。
此方案应提供来自Recent
的有效近期项目查询,并在Archive
中安全地长期存储,并在给定所需日期范围的情况下以合理的Archive
查询效率。主要缺点是每个项目都要写两次(每个容器一个)-但这是有效轮询的折衷方案。这种权衡是否值得,最好通过模拟负载并观察性能来确定。