Erlang/Elixir 谈一点 Event Sourcing 和 CQRS

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

这两个概念是与 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 蹲个话题扩展的后续😀

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