CQRS架构模式:不只是简单的读写分离
你有没有遇到过这样的情况?一个电商平台,用户下单特别频繁,但查看订单详情的请求更多。结果一到促销节,数据库直接卡死——写的压力大,读的也扛不住。这时候,普通开发者可能想到加索引、分库分表,但其实还有更巧妙的解法:CQRS。
CQRS 全称是 Command Query Responsibility Segregation,翻译过来就是“命令与查询职责分离”。听起来挺学术,其实理念很简单:把“改数据”的操作和“查数据”的操作彻底分开。
为什么需要CQRS?
传统应用里,同一个接口既要处理创建订单,又要查询订单列表,全都走同一张表。时间一长,表结构越来越臃肿,性能也越来越差。比如你在后台加个统计字段,结果发现每次更新都会影响查询速度。
CQRS 的思路是:写一套模型专门处理命令(比如提交订单),另一套模型专门服务查询(比如展示订单)。两者可以使用不同的数据库、不同的表结构,甚至完全独立部署。
一个实际场景
假设你开发一个博客系统。用户发文章时,系统要做校验、触发通知、更新标签统计;而读者看文章时,只需要快速加载格式化后的内容。
用 CQRS,你可以这样做:
写模型接收“发布文章”命令,把原始内容存进事务型数据库(如 PostgreSQL);
同时,通过事件机制通知读模型,把渲染好的 HTML、作者信息、阅读数等组装成一张扁平化的视图,存进更适合查询的数据库(如 Elasticsearch)。
这样一来,读者访问页面飞快,而后台复杂的逻辑不会拖慢前端体验。
代码怎么体现?
下面是个简化版的结构示意:
public class CreateArticleCommand {
public string Title { get; set; }
public string Content { get; set; }
}
public class ArticleQuery {
public int Id { get; set; }
public string Title { get; set; }
public string HtmlContent { get; set; }
public string AuthorName { get; set; }
public DateTime PublishedAt { get; set; }
}命令和查询对象完全不同,各自优化。处理命令的地方专注一致性,处理查询的地方专注响应速度。
不是所有项目都适合
别一听高大上就立刻搬进项目。小系统用 CQRS 反而增加复杂度。比如你只是做个个人记账工具,读写都在本地 SQLite,硬拆成两套模型,维护成本反而上去了。
它更适合那些业务复杂、流量大、对响应时间敏感的系统,比如电商后台、社交平台动态流、实时报表系统。
另外,由于读写模型异步同步,可能会有短暂的数据不一致。比如你刚发布文章,刷新页面却没看到——这在 CQRS 中叫“最终一致性”,得在产品设计上做好引导,比如提示“正在发布中”。
搭配事件驱动更强大
CQRS 经常和事件溯源(Event Sourcing)一起出现。每次命令执行后,不是直接改状态,而是记录一个事件,比如“文章已发布”。读模型监听这些事件,逐步构建自己的视图。
这种组合让你能追溯每一次变更,还能方便地重建数据视图,适合审计、回滚、数据分析等场景。
技术没有银弹,但 CQRS 提供了一种新的思考角度:当读和写开始打架,不妨把它们分开养。