下载工作台
企业级架构运维

数据一致性与消息驱动

试读上半部分 · 解锁后可读全文

第 5 章 · 数据一致性与消息驱动

本章讲解分布式下的 一致性模型、本地/分布式事务、Saga、事务消息、事件驱动架构(EDA)与幂等设计。

前置:PaaS 消息组件 ch4/ch9;架构第 4 章微服务边界。


5.1 一致性光谱

模型说明典型场景延迟窗口
强一致读到的永远是最新已提交写账户余额、库存扣减同步完成即一致
最终一致短暂不一致,稍后对齐搜索索引、报表、推荐秒~分钟
因果一致有因果关系的事件有序可见订单状态流、评论回复同 key 有序
读己之所写用户看到自己刚写的数据发帖后立即刷新会话粘滞或主库读

CAP 回顾:分区(P)发生时,只能在 一致性 C可用性 A 间权衡;多数互联网业务选 AP + 最终一致,金融核心选 CP

业务推荐模型理由
支付扣款强一致不能超扣
商品搜索最终一致几秒滞后可接受
订单状态展示因果一致不能先显示「已发货」再「待支付」
点击量统计最终一致近似即可

5.2 本地事务 vs 分布式事务

方案适用复杂度TPS 影响贤紫组件
单库事务单体或未拆库MySQL InnoDB
2PC / XA强一致金融(少用)很高显著下降
Saga长事务、可补偿状态机 + MQ
TCC预留/确认/取消自研 + Redis 预留
事务消息本地写 + 发消息原子RocketMQ
Outbox可靠发事件定时投递

选型决策树

同一服务同一库? ──是──► 本地事务
        │
        否
        ├── 需强一致且步骤少? ──► TCC(慎用)
        ├── 长流程可补偿? ──► Saga
        └── 本地写后要发消息? ──► 事务消息 / Outbox

5.3 Saga 模式(推荐掌握)

5.3.1 编排 vs 协同

类型协调者优点缺点
编排(Orchestration)中央状态机 / order-saga流程清晰协调者单点需 HA
协同(Choreography)各服务听事件解耦难追踪全局状态
创建订单 ──► 预占库存 ──► 创建支付单 ──► 支付成功 ──► 确认库存 ──► 发通知
     │            │              │
     └── 失败则逆序补偿 ◄─────────┘
         cancel    release      void-pay
步骤正向服务正向动作补偿动作幂等键
1ordercreate(PENDING)cancelorderId
2inventoryreservereleaseorderId
3paymentcreate + payrefundpaymentId
4inventoryconfirmorderId
# Saga 状态机片段(概念)
TRANSITIONS = {
    "CREATED":      {"reserve_ok": "STOCK_RESERVED", "fail": "CANCELLED"},
    "STOCK_RESERVED": {"pay_ok": "PAID", "fail": "COMPENSATING"},
    "COMPENSATING": {"done": "CANCELLED"},
}

行业案例 · 某跨境电商:用同步 REST 链「下单→扣库存→调支付」,支付超时 30s 导致库存 长期预占。改 Saga + 15min 超时自动 release,预占泄漏下降 95%


5.4 事务消息(RocketMQ)

┌─────────────┐    1. 发半消息     ┌──────────┐
│ order-api   │ ────────────────► │ RocketMQ │
│             │    2. 本地事务写库  │          │
│             │ ◄── 3. commit/rollback
└─────────────┘    4. 消费者可见   └──────────┘
// RocketMQ 事务消息监听器(概念)
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
    try {
        orderService.createPending(order);
        return LocalTransactionState.COMMIT_MESSAGE;
    } catch (Exception e) {
        return LocalTransactionState.ROLLBACK_MESSAGE;
    }
}
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
    return orderService.exists(msg.getKeys())
        ? LocalTransactionState.COMMIT_MESSAGE
        : LocalTransactionState.ROLLBACK_MESSAGE;
}
要点说明
回查网络抖动时 Broker 回查本地事务状态
幂等消费者按 orderId 去重
与 KafkaKafka 事务更适合流处理;业务事务消息常用 RocketMQ(PaaS ch9)

5.5 Outbox 模式

-- 同一本地事务内
BEGIN;
  INSERT INTO orders (...) VALUES (...);
  INSERT INTO outbox (event_type, payload, created_at)
    VALUES ('OrderCreated', '{"orderId":"O1"}', NOW());
COMMIT;

-- 独立投递进程轮询 outbox,发 MQ 后标记 sent
对比事务消息Outbox
MQ 绑定强依赖 Broker 事务任意 MQ
实现Broker 原生多一张表 + 投递器
延迟轮询间隔(通常 < 1s)

5.6 事件驱动架构(EDA)

OrderCreated 事件 ──► inventory 订阅(预占)
                 ──► points 订阅(加积分)
                 ──► notification 订阅(发短信)
                 ──► search 订阅(更新索引)
优点缺点治理要求
解耦调试链路长统一 traceId
易加消费者顺序/重复难幂等 + 分区 key
削峰最终一致窗口监控 lag

5.6.1 事件规范

{
  "eventId": "evt_8f3a2b1c",
  "eventType": "OrderCreated",
  "occurredAt": "2026-08-18T10:00:00Z",
  "aggregateId": "O20260818001",
  "version": 1,
  "payload": {
    "userId": "U100",
    "items": [{"sku": "SKU1", "qty": 2}]
  },
  "traceId": "trace-abc"
}
规则说明
命名过去式 OrderCreated
eventId全局唯一,幂等键
version载荷演进
traceId链路关联

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。