Erlang/Elixir 谈一点 Event Sourcing 和 CQRS

chenge · 2021年06月02日 · 最后由 niucang 回复于 2021年11月17日 · 1699 次阅读

这两个概念是与 Domain 驱动设计相关的,最近稍微研究了一下。

这个图来自微软的免费书:Exploring CQRS and Event Sourcing,2013 年的,国外似乎已经研究多年了。

Event Sourcing 主要的思想是记录事件,没有修改和删除。事件 replay 得到结果,有点函数型的数据不变的意思。

另外,利用 kafka 传递消息可以异步方式解耦服务,特别适合于微服务架构。

CQRS 是读写分离。

当然这个还是比较复杂的东西,我尝试了 Elixir commanded 框架,不过没有成功。

不知道 ruby 社区是否有这方面的框架和尝试?

那块没有成功

对 duobei 回复

什么意思?我看到 ruby 有一个框架,不过 star 不多。

Event Sourcing 主要的思想是记录事件,没有修改和删除。事件 replay 得到结果,有点函数型的数据不变的意思。

我的理解 Event Sourcing 是个状态机。下游通过持续不断的 apply event,获得和上游一样的状态。

对 hww 回复

elixir 的 commanded 的 star 一千多,我尝试过,不过我没有成功。

sequent 怎么样,学习容易吗?

CQRS 最重要的思想是读写异构,即数据的写入模型和读取模型是不同构的。

这一点是相对于传统架构来说的。

举个例子,我们有一个用户表,包含三个字段 id, name, gender。在传统架构里,我们会在 RDS 里定义这么一个数据表,当有新用户时我们插入包含 id, name, gender 的用户数据到这张表;当有查询请求时我们又从同一张数据表里将 id, name, gender 取出来返回给调用方。这种模型我们称为读写同构。

那么读写异构到底是什么。

比如说,接着上面的例子。我们还是有一个 RDS 用户表包含 id, name, gender。当有新用户注册时我们插入包含 id, name, gender 的用户数据到这张 RDS 用户表里;当有查询请求时,我们不从这张表直接读取数据了,而是从另一个数据存储器里读取数据(例如 redis,mongo, es 等)- 暂时叫它“只读数据库”。

这有什么不同呢?

不同之处在于,只读数据库可以有多个(以满足多样化的查询需求),且只读数据库中的数据模型跟 RDS 用户表的模型(即 id, name, gender)可以不一样。例如其中一个只读数据库是用于统计所有 gender=男(或女)的用户数;另一个只读数据库是用于统计姓“李”的用户的数量,等等。应该可以想象这两个只读数据库模型跟 RDS 用户表的不同。这就是读写异构。

为什么要读写异构?

还是以刚才的例子。想象一下在传统方案里,要实现统计所有姓“李”的用户数量这个查询,我们需要做什么?性能又如何?跟直接从第二个只读数据库中查询相比复杂程度如何?这仅仅只是其中一种查询需求,在现实需求中有更多且更复杂的查询需求,数据同构方案如何保持数据模型的可维护性和高效性?(方案肯定是有的,但必然陷入成本、复杂性、高效性三角形取舍中)

说了好处,再说说它为了解决问题而引入的新问题:多个数据库之间如何同步?

还是接着刚才的例子。现在需要把 RDS 用户表的数据同步到其他数据库,一般会有几种情况:目标数据库跟源库是同族;目标数据库跟源库不同族,但有官方或可靠的插件可实现实时同步;目标数据库跟源库差别较大。第一、二种情况比较简单,可以简单用 binlog 或者插件实现同步;一般复杂度在于第三种情况,比如源库是 mysql/posgres,目标库是 es、mongo、redis 等。ETL 是其中一种方案,这里就不展开了。

为什么 CQRS 通常会跟 event sourcing 一起被提起?

event sourcing(即事件溯源)是一个独立概念,实现技术和方案有很多(可以搜到,不一一列举了)。event sourcing 是解决多数据库之间同步问题的一个比较好的方案。特别是在高可扩展性系统里,不仅解决了同步问题,还实现了系统的高可扩展性。比如全新的业务模块需要同步已有模块中的某维度的数据。有了事件溯源,只需要从头把事件轴在新模块里重放一遍,数据就同步到新模块的数据库中了。这个过程对原业务完全透明,也不会因同步对源库造成额外压力。这种方式保证了现有业务的稳定性,也满足了系统的良好扩展性。由此可见,event sourcing 在 CQRS 中的作用。

码一下,迟点再补充。可能会扩展以下话题

  • CQRS 一定要搭配 event sourcing 吗?
  • golang 在基础架构和中间件的崛起及对 CQRS 的影响
  • serverless 和事件驱动的分布式架构
  • 想到再补充
对 eedkevin 回复

最近遇到一个查询性能问题,最后通过创建了一个缓存的视图解决了,这是不是就是读写分离了?

CQRS:TypeORM 支持 读写异构不同数据库。

Event Sourcing:这个麻烦了,修改和删除必须通过新增一条数据来实现,查询结果为快照。目前有些数据库实现了。类似于 Git 版本控制,随时可以数据溯源或还原。一般 MQ 用得比较多。

对 zhongsheng 回复

是读写异构所表达的思想,不过 CQRS 更侧重于解决分布式系统中的一些问题,在单一数据源/库场景里反而没什么发挥的空间

没必要纠结是不是 CQRS,只要理解了读写异构的本质和它提供的解决问题的思路就好了。关于这个有空我再补充下原回答

@eedkevin 蹲个话题扩展的后续😀

需要 登录 后方可回复, 如果你还没有账号请 注册新账号