微服务跨库事务乱套?Saga/CQRS/ 事件溯源,三大模式根治一致性难题
·
微服务架构设计模式 第四部分:数据篇
数据一致性模式
在微服务架构下,由于数据库分散在不同服务中,跨服务的操作无法依赖单一数据库的 ACID 事务来保证一致性。
因此,需要通过 分布式一致性模式 来实现 最终一致性。
1 Saga Pattern(长事务协调)
核心思想
- 将一个 分布式长事务 拆分为一系列 本地事务。
- 每个本地事务完成后,触发下一个事务。
- 如果某一步失败,执行相应的 补偿操作(Compensation)。
示例场景:订单支付
- 创建订单(order-service)
- 扣减库存(inventory-service)
- 扣减余额(account-service)
- 如果余额不足 → 回滚库存 & 取消订单
代码示例(简化版)
@Service
public class OrderSagaService {
private final InventoryClient inventoryClient;
private final AccountClient accountClient;
public void placeOrder(Long orderId, Long userId, Long productId) {
try {
// Step 1: 创建订单
createOrder(orderId, userId, productId);
// Step 2: 扣减库存
inventoryClient.reduceStock(productId);
// Step 3: 扣减余额
accountClient.debit(userId, 100);
// Step 4: 标记订单成功
markOrderSuccess(orderId);
} catch (Exception ex) {
// 补偿逻辑
inventoryClient.rollbackStock(productId);
cancelOrder(orderId);
}
}
}
➡️ 这里的 补偿事务 取代了数据库回滚,保证最终一致性。
2 CQRS(命令查询职责分离)
核心思想
- 写操作(Command) 和 读操作(Query) 使用不同的数据模型。
- 写模型 → 强一致性,负责修改。
- 读模型 → 弱一致性,经过事件同步后可供快速查询。
架构示意
[Command API] --> [Write DB] --> [Event Bus] --> [Read DB] <-- [Query API]
示例:订单系统
- 用户下单(Command):写入 订单数据库,并发布
OrderCreatedEvent。 - 查询订单(Query):从 订单查询视图数据库(如 ElasticSearch、Redis)读取,保证高性能。
Spring 示例(简化版)
// 写模型
public class CreateOrderCommand {
private Long userId;
private Long productId;
}
// 事件监听更新读库
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
orderViewRepository.save(new OrderView(event.getOrderId(), event.getUserId(), "CREATED"));
}
➡️ CQRS 牺牲了一定的 实时一致性,换取更高的 查询性能 与 可扩展性。
事件溯源(Event Sourcing)
核心思想
- 系统状态并不是直接存储的,而是通过 事件序列 重放出来的。
- 每个状态变化都是一个事件,例如
OrderCreated,OrderPaid,OrderShipped。 - 系统状态 = 所有事件的累积结果。
优点
✅ 自然支持 审计和回溯(历史全保留)。
✅ 与 CQRS 天然契合(事件即是读库更新的来源)。
✅ 更容易实现复杂的业务补偿。
示例:订单服务
public class OrderAggregate {
private List<OrderEvent> changes = new ArrayList<>();
public void apply(OrderCreatedEvent event) {
// 修改状态
this.changes.add(event);
}
public void apply(OrderPaidEvent event) {
// 修改状态
this.changes.add(event);
}
// 通过事件重放恢复订单状态
public void replay(List<OrderEvent> events) {
events.forEach(this::apply);
}
}
➡️ 数据库中保存的不是订单行,而是订单的 所有事件。查询时通过 事件重放 得到订单状态。
4 总结
- Saga Pattern:通过 补偿事务 实现跨服务的最终一致性,适合长事务场景。
- CQRS:读写分离,提升读性能,适合高并发查询的系统。
- 事件溯源:通过事件重放保证一致性和可追溯性,适合审计要求高的场景。
更多推荐



所有评论(0)