-
浅谈CQRS
大背景
- 基于事件驱动的微服务架构
在微服务的大背景下,服务拆分、服务通信、服务事务管理成为需要解决和研究的课题。服务的查询是其中一个,服务拆分后,联合查询成为一大问题。
为什么
服务拆分后的通常查询措施
- API组合模式
检索多个服务所拥有的数据,通过调用拥有数据的服务并组合结果来实现查询操作
(大白话:哪个表/库/服务有我想要的数据,就调用那个表/库/服务的查询接口,拿到数据后通过编写代码将其组合成业务所需数据)
在单体应用中,当联合查询不足以满足业务数据需求的时候,也会用到这种模式。在微服务中没有了“跨库联合查询”,将API组合模式搬过来就显得“理所当然”了
组合的开销成本
数据组合需要调用多个服务,网络、数据集操作等多因素造成的开销大大增加查询的耗时。这时候spring reactive stack就排上了一定用用场,将编程模式从同步转到异步。
组合的职责问题
服务拆分后,各服务职责单一几乎已经成为“政治正确”,但是往往查询并不是单个服务的查询,而是queryXxxDetail()这样多服务的数据整合。
API Gateway,严格来讲并不是所谓的API组合模式,从结果来看确实是组合了API,但它却将组合的操作下放到了service,使service不再“纯粹”。组合的数据一致性
查询数据一致性通过组合API模式查询的服务间缺少事务支持,进行多数据库查询会出现部分数据不一致的情况。虽然办法总比困难多,但这不是写业务的程序员应该考虑的事情,也不应该徒增代码的复杂度。
是什么
什么是CQRS(命令查询职责隔离)
理解概念就先了解定义CQRS(Command Query Responsibility Segregation)有四个字母,分开说
C和Q
C和Q的具体
- Pressing a command button is a way to make the machine change state.but you can press a query button. This does not change the state.
C和Q的抽象
- A command (procedure) does something but does not return a result.
- A query (function or attribute) returns a result but does not change the state.
这里command翻译成指令或许更好理解一点,综上就是:
command:改变状态,但不返回结果query:返回结果,但不改变状态
然后放在应用程序里,对应过来就是CUD和R。(当然会有改变状态并返回结果的,如保持实体并返回实体ID)
R&S
字面意思,就是C(Command)和Q(Query)的职责(Responsibility)隔离(Segregation),没什么需要强加的解释。
CQRS
CQRS是命令查询职责隔离( Command Query Responsibility Segregation) 的简称,顾名思义,它涉及隔离或问题的分隔。它将持久化数据模型和使用数据的模块分为两部分:命令端和查询端。命令端模块和数据模型实现创建、更新和删除操作(缩写为CUD,例如: HTTP POST、PUT和DELETE)。查询端模块和数据模型实现查询(例如HTTP GET)。查询端通过订阅命令端发布的事件,使其数据模型与命令端数据模型保持同步。

CQRS将原本的CRUD彻底分为CUD和R(类似将Elasticsearch或Solr文本搜索数据库文本搜索查询,在应用程序层面再竖切一刀),即命令端模块和查询端模块,同时将查询端数据库从物理层面分开,作为视图数据库,查询端模块订阅命令端模块的事件并更新(同步)视图数据库,至于视图数据库用什么数据库取决于业务需求。(这就有点从数据仓库(DW)到数据集市(DM)的意思)
- 此处
视图数据库(view database)并非数据库视图(database view)
视图数据库(view database):对数据库职责的抽象
数据库视图(database view):对数据库数据的抽象 R分离出来并不意味着查询模块部分没有CUD(数据持久化)
怎么样
- 架构隔离和可扩展性
视图数据库并没有指定具体数据库,完全取决于业务需求,所以就可以“为所欲为”了。就算修改视图结构也不会影响原始数据。
- 数据查询更高效和简单
CUD和R分开后,一方面解决在读与写负载不均的情况(通常读大于写),R分离出来后,视图数据库可以自由缩放;另一方面数据整合已经在查询端模块的事件处理程序完成,查询和数据的获取就变得简单多了。
- 事务数据最终一致性
在视图数据库支持事务模式的前提下,事件处理程序持久化的数据最终是一致的。(保证事件有序)
最后
“没有一项技术可以被称为‘银弹’”。从大前提(基于事件)的部署,到如何设计CQRS视图的细节都需要考虑细致,还有团队和成本,到最终的落地。架构设计应适应业务发展,杀鸡用牛刀,杀前还得磨刀,杀完把鸡扔了,却只取了个鸡蛋,那就舍本求末乱套了。
参考
- Bertrand Meyer.Object-Oriented Software Construction 2nd Edition[M].Prentice Hall,1997:751
- Bertrand Meyer. Eiffel: a language for software engineering,2014:24
- Chris Richardson.Microservice Patterns[M].Manning Publications,2019:229
- (美)克里斯·理查森(Chris Richardson),喻勇.微服务架构设计模式[M].机械工业出版社,2019.4:212-243