一、你的系统是不是也遇到了这些问题?
订单系统越来越慢了——用户查询订单要等好几秒,而且经常出现数据不一致的问题。更糟糕的是,每次优化查询性能,都会影响订单创建的性能;优化订单创建,查询又变慢了,陷入死循环。
二、传统架构的困境
在传统的CRUD架构中,读写操作都在同一个数据库上进行,相互影响:
- 读写冲突:写操作需要加锁,影响读性能;读操作占用连接,影响写性能
- 性能瓶颈:添加索引、分库分表都增加系统复杂度
- 数据一致性:多个服务同时操作同一份数据,容易出现不一致
- 审计困难:只知道数据的当前状态,不知道数据如何变成这样的
三、事件溯源 + CQRS:架构设计的革命
3.1 事件溯源(Event Sourcing)
传统CRUD存储的是数据的当前状态(就像拍照片),而事件溯源存储的是导致状态变化的事件序列(就像录像带,可以回放整个过程)。
3.2 CQRS(Command Query Responsibility Segregation)
核心思想是将写操作(Command)和读操作(Query)分离:
- 写操作:接收命令 → 验证 → 发布事件 → 存储事件
- 读操作:消费事件 → 构建物化视图 → 高效查询
四、实战实现
4.1 事件定义
// 订单创建事件
public class OrderCreatedEvent {
private String orderId;
private String userId;
private BigDecimal amount;
private List items;
private LocalDateTime createTime;
}
// 订单支付事件
public class OrderPaidEvent {
private String orderId;
private String transactionId;
private LocalDateTime payTime;
}
// 订单取消事件
public class OrderCancelledEvent {
private String orderId;
private String reason;
private LocalDateTime cancelTime;
} 4.2 事件存储
CREATE TABLE event_store (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
aggregate_id VARCHAR(64) NOT NULL,
event_type VARCHAR(64) NOT NULL,
event_data JSON NOT NULL,
version BIGINT NOT NULL,
create_time DATETIME NOT NULL,
INDEX idx_aggregate (aggregate_id, version)
);4.3 命令处理
@Service
public class OrderCommandService {
public void createOrder(CreateOrderCommand command) {
String orderId = UUID.randomUUID().toString();
OrderCreatedEvent event = new OrderCreatedEvent();
event.setOrderId(orderId);
event.setUserId(command.getUserId());
event.setAmount(command.getAmount());
eventStore.saveEvent(orderId, "OrderCreated", event, 1);
eventPublisher.publishEvent(event);
}
}4.4 查询投影
@Component
public class OrderProjection {
@EventListener
public void on(OrderCreatedEvent event) {
OrderReadModel model = new OrderReadModel();
model.setOrderId(event.getOrderId());
model.setStatus("CREATED");
readRepository.save(model);
}
@EventListener
public void on(OrderPaidEvent event) {
readRepository.updateStatus(event.getOrderId(), "PAID");
}
}五、关键设计考量
- 快照管理:定期创建聚合快照,避免重放过长事件链
- 幂等性:消费者基于eventId去重,确保重复消费安全
- 版本管理:事件结构演化时需支持向后兼容
- 最终一致性:设计合理的SLO(如99.9%的事件在500ms内投递),建立监控告警
六、适用场景与局限性
CQRS+ES适用于:审计要求高、读写比差异大、需要事件回溯的业务系统。不适合:简单的CRUD应用、对强一致性要求极高且不可妥协的场景。选择前务必评估团队能力——该架构将系统复杂度提升了一个数量级。
评论 (0)