CQRS 数据存储方法:NoSQL 或 SQL Server。

17

我正在进行一个新项目的研究阶段,目前正与一位同事就该项目的架构进行辩论。

我们已经达成共识,将使用CQRS和事件溯源在Azure上创建一个分布式消息传递系统。它将是一个SPA,前端使用Angular JS,后端将是Web API。

我们现在讨论数据库如何设置,这就是不同之处。

我们已经同意将数据库分为两个,一个用于读取,一个用于写入。我的同事想要在读写数据库中都使用SQL Server,因为他整个职业生涯都在使用SQL,不想听其他任何东西。而我则一直在研究NoSql,认为它更适合用于读取数据库,因为它在性能方面更好。

由于CQRS都是关于最终一致性的,我了解到NoSql数据库也基于这一点,这使我开始考虑将NoSql用于写入数据库。

我们还计划为每个聚合根创建一个事件表,而不是包含所有事件的通用事件表。由于这些表不是关系型的,这让我想知道为什么我们应该使用SQL Server。

我的问题更多地是关于人们如何创建他们的事件存储的最佳实践或一般方法。

  1. 您是将所有事件用一个表存储,还是为每个聚合根创建一个事件表?
  2. 对于CQRS应用程序,使用NoSql数据库作为读写数据库的优缺点是什么?
  3. 最后但并非最不重要的,您如何说服一个固执己见、一心只用SQL的同事去接受NoSql方法?
3个回答

16

注意:我下面的答案是来自2014年。时代已经改变,现在有几个NoSQL数据库支持多文档事务。我仍然推荐使用PostgreSQL来存储事件,特别是因为它拥有极好的JSON支持。


最后一个问题主要是基于观点的,所以让我们从我的角度回答前两个问题。

在此之前,我想说我们正在使用一个SQL服务器(Postgres)作为我们的事件存储。使用CQRS+ES,将主要写入操作集中在一个主节点上,并使存储库从从节点读取(不要与CQRS读取混淆),这一点非常容易实现。在这种设置下,可扩展性不应该是一个问题(如果Reddit可以在主/从模式下使用Postgres进行扩展,那么你也可以)。

我对各种NoSQL存储选项进行了广泛评估。最终,对于一个CQRS+ES设置,我无法找到任何优势能够超过使用SQL方式的主/从模式,而且在我看来,使用NoSQL数据库会带来更多的劣势。

特别是,在SQL存储中实现乐观锁定是非常容易的(在streamId和version字段上设置唯一约束),但是在简单的NoSQL键值存储中实现可靠的乐观锁定则相当困难(甚至可能是不可能的)。

有些文档存储库是可以实现的(我认为NEventStore仅支持RavenDB和MongoDB是有原因的):

  1. RavenDB支持ACID事务,但您必须使用/部署.NET,这对某些人(包括我们在内)不是一个选择。
  2. MongoDB具有服务器端原子操作,您需要将其用于将事件附加到事件流并在原子操作中提高版本号,但存在一些技术限制(特别是最大文档大小),这让我远离MongoDB作为事件存储。此外,我阅读了关于分区容错性的this article,Postgres似乎比MongoDB更加可靠。

在Postgres中,我为所有事件使用一个单独的事件表。

关于第二个问题,我认为你需要区分事件存储和读模型。我们使用SQL服务器作为事件存储(出于上述原因),但对于CQRS的读模型,我认为NoSQL“数据库”非常适合,因为最终一致性方法非常符合NoSQL范例。此外,人们通常只通过键来查询读取侧面,所以您可以使用任何适合您需求和喜欢的键值存储。我们甚至没有使用数据库,而是使用按需重新构建的内存网格。

https://dev59.com/umw05IYBdhLWcg3wuUKZ 的被采纳答案对于表格建模有一些很好的评论。 - Alexander Langer

6

个人而言,我更喜欢每个表只有一个聚合,但这取决于具体情况。即使使用一个表来存储所有聚合,也可以使用分区技术来缓解潜在的性能问题。

我认为NoSQL对于读写两端都是可行的,但SQL数据库提供了一项实用的功能:事务性。当您需要在一个事务中提交多个事件时,这尤其有用。对于NoSQL数据库,您可能需要更改模式才能实现此目的。例如,在采用Mongo时,使用每个文档每个事务。或者您可以使用Cassandra,并将每一行作为一个聚合(每列一个事件,Cassandra为行级提供部分事务性)。

但是,如果您的程序每次只发布一个事件或一致性不是强制要求,则这并不是一个很大的优势。


2

即使您不使用FK(关系),这也使得SQL对事件溯源来说是“过度杀伤力”。在SQL中,FK并非强制要求。

NoSQL是一个很酷的概念,但在我的个人经验中,我发现在所有方面与SQL一起工作更加容易。

不要想太多。使用SQL。让你的同事自己做决定 :)(开玩笑),然后完成工作。归根结底,我们大多数的“头痛”都是因为我们无法抵制新技术/经验的诱惑。

有意义吗?


上次我想到这个问题时,同事们在SQL方面遇到了很大的问题,我不得不使用bcp来解决。如果你的规模较小且没有生成大量事件,则事件=插入性能不是SQL的强项。 - user1496062
我开始相信,使用SQL数据库作为事件存储是必要的,因为您必须原子性地将应用程序状态更改(在事件存储中)与表示该状态更改的MOM事件消息(在发件箱中)持久化。如果您今天不使用消息传递,则以后可能会使用它。 - ClemC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接