将 PartitionKey 编码为文档 ID?

Encode PartitionKey into Document Id?

我已将我的一个 Cosmos DB 的分区键设置为 /partition

例如:我们有一个 Chat 文档,其中包含 订阅者 的列表,然后我们有 ChatMessages 包含一个文本,对作者和其他一些属性。两个文档都有一个 partition 属性,其中包含类型 'chat' 和聊天 ID。

聊天 示例:

{
"id" : "955f3eca-d28d-4f83-976a-f5ff26d0cf2c",
"name" : "SO questions",
"isChat" : true,
"partition" : "chat_955f3eca-d28d-4f83-976a-f5ff26d0cf2c",
"subscribers" : [
    ...
]
}

然后我们有 Message 个这样的文档:

{
"id" : "4d1c7b8c-bf89-47e0-83e1-a8cf0d71ce5a",
"authorId" : "some guid",
"isMessage" : true,
"partition" : "chat_955f3eca-d28d-4f83-976a-f5ff26d0cf2c",
"text" : "What should I do?"
}

现在很方便return一个特定聊天的所有消息,我只需要用属性isMessage = true查询分区chat_955f3eca-d28d-4f83-976a-f5ff26d0cf2c的所有文档.一切顺利...

但是如果我现在想通过 id 查询我的数据库中的特定消息,我通常只知道 id,但不知道分区,因此必须 运行 a slowcrosspartition查询。这让我想到了一个问题,即我是否不应该将 partitionKey 添加到消息 ID 中,以便在查询数据库时拆分 id 以进行更快的查找。我看到文档的 _rid 属性 看起来像是数据库 ID 和集合 ID 的组合,然后是文档特定 ID。我的意思是(简化):

Chat.Id = "abc"
Chat.Partition = "chat_abc"  //[type]_[chatId]
Message.Id = "chat_abc|123" //[Chat.Partition]|[Message.Id]
Message.Partition = chat_abc //[Chat.Partition]

假设我现在想通过id获取Message文档,我只是将id拆分为|符号,然后查询文档以 id 的第一部分作为分区,完整 ID 作为键。

这有意义吗?有更好的方法吗?我是否应该始终传递文档的 partitionKey,而不仅仅是 id?我应该只使用 _rid 属性吗?

非常感谢任何经验!

更新

我找到了以下答案

Some applications encode partition key as part of the ID, e.g. partition key would be customer ID, and ID = "customer_id.order_id", so you can extract the partition key from the ID value.

我已经通过电子邮件进一步询问 cosmos 团队这是否是推荐的模式,并 post 一个答案,以防我得到任何答案。

首先,如果您知道您的数据(和索引)大小)将保持在 10gb 限制内并且您 RU/sec 限制没问题,那么固定 partition-less 集合将绕过这个问题。可能 OP 有意做出了需要分区的决定,但出于一般化目的,这是一个需要注意的重要考虑因素。如果可能的话,KISS ;)

如果分区是必须的,那么 AFAIK 你无法避免 crosspartition 拆分及其开销,除非你知道分区键。

恕我直言,将重复的分区键合并到 id 字段中的 OP 建议是一个相当丑陋的解决方案,因为:

  1. 名称id表示它是唯一键,分区键不是它的一部分,也不是这个键及其唯一性所必需的。上游使用此密钥的任何人都将承担更长密钥的强制超额成本,无法使用更简单的 Guid 类型等。
  2. 如果您的分区键将来更改,它将变得一团糟。
  3. 合并后的内部结构 id 如果没有文档就不会很直观 - 它的部分没有命名,即使它们看起来有一个模式,新的开发者也会在没有找到外部文档以可靠地了解正在发生的事情的情况下不确定。
  4. 您的 数据模型不需要这种 语义级别的重复,这将是为了您的应用程序查询舒适,因此此类 hack 应该属于您的应用程序代码,而不是数据模型。如果可能,应避免此类泄漏问题。
  5. 文档中的数据重复会不必要地增加文档大小、带宽等(可能显着也可能不显着,具体取决于规模和使用情况)。 in-document 有时需要复制,但恕我直言,在这种情况下不一定。

更好的设计是确保分区键始终存在于逻辑上下文中并且可以传递给查找。如果您没有它,那么也许您应该重构您的应用程序代码(而不是数据设计)以在需要时显式传递 chatIdid。那就是 WITHOUT 将它们合并成某种不透明的字符串格式。

此外,我没有看到为此使用 _rid 的好方法,好像我没有记错,它不包含对分区或分区键的任何内部引用。

免责声明: 我无法访问或深入了解内部 CosmosDB 索引设计或分区集合的 _rid 逻辑。我可能误解了它的工作原理。

是的,您提出的从 id 中提取分区键(通过 prefix/delimiter 之类的约定)的建议很有意义。这在具有单个密钥并希望重构它以使用来自不同存储系统的 Cosmos DB 的应用程序中很常见。

如果您是从头开始构建应用程序,则应考虑通过 API/application 连接复合键(分区键 + 项目键 ("id"))。