事件溯源(Event Sourcing)中部分事件更新需存储哪些信息?
事件存储更新方案选型
两种写法都在生产环境有大规模应用,没有绝对的正确答案,完全取决于你的业务诉求,以下是两种方案的优劣势、适用场景以及更通用的生产实践参考:
方案一:每次事件存储全量合并后的完整数据
即你举例的version=2时携带全量字段的写法:
id: 123 version: 2, data: { name: "Bob", age: 21, rejected: true } topic: 'candidate' action: 'update'
- 优点:
- 下游消费完全不需要做聚合计算,拿到任意版本的事件就能直接得到该版本对应的实体完整状态,不管是做数据同步、版本对比还是异常排查,都不需要回溯之前的事件,开发和维护成本极低
- 事件回放、重建状态的速度极快,不需要做任何字段合并逻辑
- 缺点:
- 存储冗余度高,如果实体字段很多、更新很频繁,会重复存储大量没有变化的字段
- 写入事件前必须先拿到该实体上一个版本的完整状态做合并,写入链路多一次查询开销,极端高写入场景下会有性能瓶颈
- 适合场景:读多写少、下游消费方多、对查询响应速度要求高的业务,比如你提到的候选人档案这类基础数据场景,选这个方案性价比很高。
方案二:每次事件只存储本次变更的增量字段
即你举例的version=2时只携带变更字段的写法:
id: 123 version: 2, data: { rejected: true } topic: 'candidate' action: 'update'
- 优点:
- 没有冗余存储,每次只存实际修改的内容,存储成本最低
- 写入逻辑简单,不需要提前查询上一个版本的状态,直接写入变更内容即可,写入吞吐量上限高
- 天然保留了最细粒度的变更轨迹,要做操作审计、变更回滚、修改溯源的时候不需要额外做字段对比,直接读事件就能知道每次改了什么
- 缺点:
- 下游消费成本高,要得到某个版本的完整状态,必须从该实体的第一个事件开始按版本顺序依次回放所有增量,做字段合并,事件量越大,状态重建的延迟越高
- 排查问题时需要跨多条事件拼接数据,定位效率低
- 适合场景:写入频率极高、对变更审计有强需求、存储成本敏感的场景,比如交易流水、操作日志类业务。
生产环境更常用的折中方案
如果业务规模不小,不建议极端二选一,一般会混合两种方案的优势:
- 核心事件流只存增量变更,作为唯一可信数据源,保证最小存储成本和完整变更轨迹
- 后台异步任务按固定规则(比如每更新5次、或者每1小时)生成对应版本的全量状态快照单独存储
- 需要重建状态时,先找到离目标版本最近的快照,再回放快照版本之后的增量事件即可,既避免了从头回放所有事件的性能问题,也不会给写入链路增加额外负担。
注意:无论选哪种方案,同一个实体ID对应的版本号必须保证严格单调递增,不能出现版本重复、乱序的情况,否则回放状态时一定会出现数据错乱。
内容的提问来源于stack exchange,提问作者Richard Miles
相关产品推荐
相关产品推荐

