CQRS模式(命令查询职责分离) CQRSPattern

CQRS(命令查询职责分离)模式是一种软件架构模式,用于将读写操作分离,以提高分布式系统的可扩展性、可维护性和性能。它通过使用不同的数据模型和存储策略优化命令(写)和查询(读)处理,常与事件溯源结合以提供审计日志和灵活性。适用于高并发、复杂业务逻辑场景。关键词:CQRS、分布式系统、读写分离、架构设计、事件溯源、可扩展性、最终一致性。

架构设计 0 次安装 0 次浏览 更新于 3/5/2026

名称: CQRS 模式 描述: 实现命令查询职责分离,以在分布式系统中分离读写操作。

CQRS 模式 (命令查询职责分离)

概述

CQRS 将写操作(命令)与读操作(查询)分离,使各自能够独立扩展和演化。它常与事件溯源结合使用,但也可用于传统持久化。

目录

  1. 什么是CQRS
  2. 命令与查询
  3. 分离读写模型
  4. 无事件溯源的CQRS
  5. 有事件溯源的CQRS
  6. 最终一致性
  7. 读取模型投影
  8. 同步与异步投影
  9. 命令处理器
  10. 查询处理器
  11. 每个模型的数据库
  12. 物化视图
  13. 实现模式
  14. 何时使用CQRS
  15. 反模式

什么是CQRS

CQRS 将写模型(命令)与读模型(查询)分离。这允许每侧使用不同的数据形状、存储和扩展策略。

命令与查询

  • 命令: 意图改变状态(例如,CreateOrder)。
  • 查询: 只读数据请求(例如,GetOrderSummary)。

命令应验证并强制执行不变量;查询应优化快速读取。

分离读写模型

写模型:

  • 验证业务规则
  • 产生状态变化
  • 通常是规范化的

读模型:

  • 去规范化以便快速查询
  • 可以跨聚合连接数据

CQRS 无事件溯源

使用传统数据库进行写操作和单独的读取副本或视图:

  • 写:事务型数据库
  • 读:读取优化的副本或缓存

CQRS 有事件溯源

命令侧写入事件。读取模型是从事件构建的投影:

  • 清晰的审计日志
  • 可重建的视图
  • 设计上具有最终一致性

最终一致性

读取可能滞后于写入:

  • 向客户端沟通陈旧性
  • 需要时使用读取你自己的写入
  • 提供一致的状态端点

读取模型投影

投影处理器示例:

interface OrderCreated {
  orderId: string;
  customerId: string;
  total: number;
}

function projectOrderCreated(event: OrderCreated) {
  // 插入或更新读取模型
}

同步与异步投影

  • 同步: 延迟较低,陈旧度较低。
  • 异步: 吞吐量较高,最终一致性。

基于SLA和复杂性选择。

命令处理器

命令处理器应该:

  • 验证输入
  • 强制执行不变量
  • 原子性地持久化更改
  • 如果需要,发出事件

查询处理器

查询处理器应该:

  • 使用读取优化的存储
  • 尽量避免重连接
  • 积极分页和缓存

每个模型的数据库

常见模式:

  • 写数据库:PostgreSQL/MySQL
  • 读数据库:Elastic、Redis 或去规范化表

物化视图

物化视图提供快速读取,并可通过投影或ETL刷新。

实现模式

Node.js 示例结构:

src/
  commands/
  command-handlers/
  queries/
  query-handlers/
  read-models/

Python 示例:

app/
  commands/
  handlers/
  read_models/

何时使用CQRS

适用场景:

  • 读写工作负载差异显著
  • 读取模型需要不同形状
  • 写侧有复杂业务规则

避免场景:

  • 简单CRUD已足够
  • 团队无法管理增加的复杂性

反模式

  • 为读写共享相同的ORM模型
  • 不需要时过度分离模型
  • 忽略一致性要求

相关技能

  • 09-microservices/event-sourcing
  • 04-database/database-optimization

最佳实践

命令设计

  • 使用意图揭示的名称: 命令应清晰表达用户意图
  • 验证命令: 处理前验证
  • 保持命令小巧: 每个命令单一职责
  • 包含命令元数据: 添加时间戳、用户ID等
  • 设计为幂等: 命令应安全可重试

查询设计

  • 为读取优化: 去规范化数据以快速查询
  • 使用适当的存储: 基于查询模式选择存储
  • 实现缓存: 缓存频繁访问的数据
  • 分页结果: 支持大数据集
  • 使用查询模型: 为不同用例优化分离模型

读写分离

  • 使用单独的数据库: 读写使用不同存储
  • 独立扩展: 分别扩展读写侧
  • 处理最终一致性: 接受临时不一致
  • 沟通陈旧性: 向用户告知数据新鲜度
  • 需要时使用读取你自己的写入: 满足强一致性要求

最终一致性

  • 为延迟设计: 假设读取可能滞后于写入
  • 提供状态指示器: 向用户显示数据新鲜度
  • 使用缓存失效: 更新时使缓存失效
  • 实现同步投影: 对需要一致性的关键数据
  • 监控延迟: 跟踪写入与读取可用时间

命令处理器

  • 验证不变量: 持久化前强制执行业务规则
  • 使用事务: 确保原子状态更改
  • 发出事件: 状态更改后发布事件
  • 优雅处理错误: 返回适当的错误响应
  • 记录命令执行: 跟踪命令处理以便调试

查询处理器

  • 使用读取优化的存储: 从读取副本或缓存查询
  • 避免复杂连接: 去规范化数据以减少连接
  • 实现分页: 高效支持大数据集
  • 积极缓存: 缓存频繁访问的数据
  • 监控查询性能: 跟踪慢查询

数据库配置

  • 选择适当的数据库: 匹配数据库到工作负载
  • 配置复制: 为查询侧设置读取副本
  • 实现连接池: 优化数据库连接
  • 使用适当的索引: 为读取模式索引
  • 监控数据库健康: 跟踪性能和错误

投影管理

  • 使投影可重建: 允许从源完全重建
  • 使用增量更新: 事件到达时更新投影
  • 处理投影失败: 重试失败投影
  • 监控投影延迟: 跟踪投影滞后程度
  • 版本投影: 支持多个投影版本

测试

  • 测试命令处理器: 验证验证和状态更改
  • 测试查询处理器: 验证查询结果和性能
  • 测试一致性场景: 测试最终一致性行为
  • 测试失败场景: 测试错误处理和恢复
  • 性能测试: 测量吞吐量和延迟

监控

  • 跟踪命令指标: 监控命令处理率和失败
  • 跟踪查询指标: 监控查询性能和模式
  • 监控延迟: 跟踪写入与读取可用时间
  • 设置警报: 对异常或失败通知
  • 创建仪表板: 可视化CQRS系统健康

检查清单

设计

  • [ ] 识别读写工作负载差异
  • [ ] 设计命令模型
  • [ ] 设计查询模型
  • [ ] 选择适当的数据库
  • [ ] 定义一致性要求

命令侧

  • [ ] 实现命令验证
  • [ ] 设置命令处理器
  • [ ] 配置写数据库
  • [ ] 实现事件发布
  • [ ] 添加命令日志记录

查询侧

  • [ ] 设计读取模型
  • [ ] 设置读取数据库/副本
  • [ ] 实现查询处理器
  • [ ] 配置缓存
  • [ ] 添加查询日志记录

最终一致性

  • [ ] 定义可接受延迟
  • [ ] 实现状态指示器
  • [ ] 设置缓存失效
  • [ ] 配置同步投影
  • [ ] 监控延迟指标

投影

  • [ ] 设计投影架构
  • [ ] 实现投影处理器
  • [ ] 设置增量更新
  • [ ] 配置投影重建
  • [ ] 监控投影延迟

数据库设置

  • [ ] 配置写数据库
  • [ ] 配置读取数据库/副本
  • [ ] 设置连接池
  • [ ] 配置复制
  • [ ] 优化索引

缓存

  • [ ] 识别可缓存数据
  • [ ] 配置缓存策略
  • [ ] 设置缓存失效
  • [ ] 监控缓存命中率
  • [ ] 配置缓存过期

监控

  • [ ] 设置命令指标
  • [ ] 设置查询指标
  • [ ] 配置延迟监控
  • [ ] 创建仪表板
  • [ ] 设置警报

测试

  • [ ] 编写命令处理器测试
  • [ ] 编写查询处理器测试
  • [ ] 测试一致性场景
  • [ ] 性能测试
  • [ ] 测试失败场景

文档

  • [ ] 文档化命令模型
  • [ ] 文档化查询模型
  • [ ] 文档化一致性要求
  • [ ] 创建运行手册
  • [ ] 维护API文档