微服务架构设计模式 第四部分:数据篇

数据一致性模式

在微服务架构下,由于数据库分散在不同服务中,跨服务的操作无法依赖单一数据库的 ACID 事务来保证一致性。
因此,需要通过 分布式一致性模式 来实现 最终一致性


1 Saga Pattern(长事务协调)

核心思想

  • 将一个 分布式长事务 拆分为一系列 本地事务
  • 每个本地事务完成后,触发下一个事务。
  • 如果某一步失败,执行相应的 补偿操作(Compensation)。

示例场景:订单支付

  1. 创建订单(order-service)
  2. 扣减库存(inventory-service)
  3. 扣减余额(account-service)
  4. 如果余额不足 → 回滚库存 & 取消订单

代码示例(简化版)

@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:读写分离,提升读性能,适合高并发查询的系统。
  • 事件溯源:通过事件重放保证一致性和可追溯性,适合审计要求高的场景。
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐