在 CQRS 中处理大量非规范化读取模型更新

Handling large amounts of denormalized read model updates in CQRS

我正在设计一个 CQRS 事件源系统(这不是我的第一个系统),我的读取模型被非规范化并存储在读取优化的文档数据库中 (MongoDb)。没什么特别的。现在这个特定的读取模型是一个文档,其中包含一个用户 ID 和一个潜在的大型组数组,用户所属的组:

{
  "userId": 1,
  "userName": "aaron",
  "groups": [
    {
      "groupId": 1,
      "name": "group 1"
    },
    {
      "groupId": 2,
      "name": "group 2"
    }
  ]
}

可能有成千上万的用户是一个组的成员(举个例子:想象一个 每个 工作人员都是其中成员的组)。

首先要记住我使用 CQRS 的原因是我需要 扩展我的读取(或者更确切地说,考虑到需要,以不同的方式处理我的读取避免大量连接),我 期待 大量写入。这不是我使用 CQRS 和事件溯源的 唯一 原因,但它是一个主要催化剂。

现在我遇到的问题是当有人更新组名时(我预计这会经常发生)我的读取模型需要更新。这意味着单个用户对单个数据的修改将在我的读取存储中导致成千上万的更新。

我很清楚我可以应用的所有技术来处理分派更新以避免时间耦合,但是我担心 number 的文档将按单用户修改。

我已经阅读了几个问这类问题的 SO 答案,大多数答案表明您需要取得平衡,或者不必担心大量更新。但是 IMO,这不是一个真正的选择。在这种类型的读取模型中确实没有平衡(文档的任何重新建模仍然需要组名出现 多次 ,无论它如何重新 -建模),并且简单地接受大量更新对于超快速读取存储的想法是适得其反的,因为由于几乎总是要排队的持续更新,它现在将承受严重的负载。本质上会发生什么,非规范化过程将成为瓶颈,并且队列将随着时间的推移而增长(直到用户更新组名有一些喘息的机会),并且读取将作为副作用变慢。

在有人跳起来问我是否知道会出现这个瓶颈之前,答案是"it should, but obviously I can't be sure"。但是,基于知道我正在替换的现有系统中进行了多少更改,并且记住这不是文档数据库中唯一需要更新的模型类型,我有充分的理由担心.正如我所说,还有其他几种读取模型——可能没有相同数量的更新——但仍然会增加读取存储中的写入负载。而且,读取存储只能进行这么多次写入。

我能想到两种解决方案(一种笨,一种不那么笨):

  1. 在每个文档中存储一个版本,并且更新阅读 事件发生时的模型。然后,当特定的读取发生时 文档,我检查是否过时,以及版本是否过时(由于 一个进行中的命令),我将最后的更改应用到该文档 在存储和返回之前。然而,我的直觉告诉我 无论如何,最终每个文档都会得到更新,并且 这只是为读取增加了额外的开销。我也没有 了解版本控制的实际工作方式

  2. 使用关系读取模型并进行单连接。这似乎是 最明智的选择,因为我只更新连接 table,以及所有 很好。但是读取速度不会那么快,感觉有点 比 select * 来自 tablename 方法更差。

我的问题:

是否有解决此类问题的标准技术?我提供的第二个选项是否就是我所希望的最好的?

老实说,我原以为这种类型的问题会一直发生在 CQRS 事件源系统中,在这种系统中,非规范化数据需要保持同步,但似乎缺乏关于它的讨论社区让我相信我缺少一个明显的解决方案,或者我的阅读模型需要改进。

我认为当您期望一个用户成为成千上万个组的成员时,您选择的模型是错误的。您需要从用户文档中删除组列表并坚持使用关系模型,只保留组 ID。想象一下,您的组将需要比名称更多的属性,您将再次面临同样的问题。再一次。