2. 常见分布式事务方案:2PC、3PC、TCC、Saga、本地消息表、事务消息

在分布式系统中,事务跨越多个独立节点,传统的 ACID 特性无法直接保证。本章梳理六种主流分布式事务方案的核心原理、适用场景与性能特征,为后续鉴权拦截场景下的参数调优奠定理论基础。

2.1 方案总览与对比

六种方案在一致性强度、可用性、实现复杂度上各有取舍。下表从核心机制、一致性模型、典型延迟、容错能力四个维度进行横向对比:

方案 核心机制 一致性模型 典型延迟 容错能力
2PC 协调者 + 两阶段投票 强一致性 高(同步阻塞) 弱(协调者单点)
3PC 引入超时 + 预提交阶段 强一致性(非严格) 中高 中(减少阻塞窗口)
TCC Try-Confirm-Cancel 三阶段 最终一致性 中(业务补偿) 强(业务层补偿)
Saga 长事务拆子事务 + 补偿 最终一致性 低(异步) 强(补偿幂等)
本地消息表 本地事务 + 消息表轮询 最终一致性 低(异步) 中(依赖消息表)
事务消息 半消息 + 回查确认 最终一致性 低(异步) 强(MQ 内置)
调优视角: 在鉴权拦截场景中,强一致性方案(2PC/3PC)通常用于核心凭证同步,而最终一致性方案(TCC/Saga/事务消息)更适合权限变更通知、日志审计等可容忍短暂不一致的链路。

2.2 2PC(两阶段提交)

2PC 是最经典的分布式事务协议,分为准备阶段和提交阶段。协调者向所有参与者发送 prepare 请求,参与者执行本地事务但不提交,返回 Yes/No。若全部返回 Yes,协调者广播 commit;否则广播 rollback。

2PC 两阶段提交流程 协调者 参与者 1 参与者 2 prepare Yes prepare Yes 协调者 参与者 1 参与者 2 commit ack commit ack 阻塞点:参与者持有资源锁直到收到 commit/rollback,协调者宕机则所有参与者阻塞 调优方向:缩短 prepare 阶段执行时间,设置协调者超时重试
性能陷阱: 2PC 在 prepare 阶段持有数据库行锁或表锁,高并发下锁等待时间急剧上升。鉴权场景中,若凭证表使用 2PC 同步,TPS 超过 2000 时锁冲突概率超过 15%,需配合连接池与超时参数调优。

2.3 3PC(三阶段提交)

3PC 在 2PC 基础上引入预提交(preCommit)阶段和超时机制,降低协调者单点故障导致的全局阻塞风险。参与者如果在超时后未收到协调者指令,默认提交。

  • CanCommit: 协调者询问参与者是否可提交,参与者返回预估结果(不执行事务)。
  • PreCommit: 协调者通知参与者执行事务但不提交,参与者写入 undo/redo 日志。
  • DoCommit: 协调者根据预提交结果广播 commit 或 abort。
调优参数: 3PC 的超时时间(timeout)直接影响可用性。建议将 preCommit 超时设为 2s,doCommit 超时设为 5s,避免网络抖动导致频繁回滚。在鉴权拦截中,3PC 适用于跨机房凭证同步,但需配合心跳检测。

2.4 TCC(Try-Confirm-Cancel)

TCC 将事务拆分为三个业务阶段,由业务方实现补偿逻辑,不依赖数据库锁,适合高并发场景。

阶段 操作 资源状态 失败处理
Try 预留资源(如冻结库存、锁定额度) 中间状态 直接回滚预留
Confirm 确认执行业务(扣减库存、更新余额) 最终态 幂等重试
Cancel 释放预留资源(解冻库存、退回额度) 回滚态 幂等重试
鉴权场景适配: TCC 的 Try 阶段可用于“预占令牌”或“预检权限”,Confirm 阶段真正下发凭证,Cancel 阶段释放预占资源。这种方式将锁粒度从行级降低到业务字段级,并发能力提升 3-5 倍。

2.5 Saga

Saga 将长事务拆分为多个本地子事务,每个子事务对应一个补偿事务。Saga 有两种执行模式:

  • 编排模式(Choreography): 每个子事务完成后发布事件,触发下一个子事务。无中心协调者,耦合度高。
  • 协调器模式(Orchestration): 中心协调器负责调用子事务和补偿事务,控制流程清晰,适合复杂链路。
Saga 协调器模式流程 协调器 子事务 1 子事务 2 子事务 3 补偿 2 补偿 1 子事务 3 失败 → 协调器按逆序调用补偿 2 → 补偿 1 补偿事务必须幂等,支持多次重试
补偿幂等性: Saga 的补偿事务可能被多次调用(网络重试、协调器重启)。必须在补偿逻辑中实现幂等控制,例如使用唯一请求 ID 去重表。鉴权场景中,补偿操作如“吊销令牌”需保证多次执行结果一致。

2.6 本地消息表

本地消息表方案将分布式事务拆解为本地事务 + 消息表轮询。业务操作与消息写入在同一个本地事务中完成,后台定时任务轮询消息表并发送到 MQ,下游消费后确认删除消息。

-- 本地消息表结构示例
CREATE TABLE local_message (
  id          BIGINT PRIMARY KEY AUTO_INCREMENT,
  biz_id      VARCHAR(64) NOT NULL COMMENT '业务ID',
  content     TEXT NOT NULL COMMENT '消息内容',
  status      TINYINT DEFAULT 0 COMMENT '0:待发送 1:已发送 2:已确认',
  retry_count INT DEFAULT 0,
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  INDEX idx_status (status, create_time)
);

-- 定时任务轮询待发送消息(每 1s 执行)
SELECT * FROM local_message 
WHERE status = 0 AND retry_count < 5 
ORDER BY create_time ASC LIMIT 100;
调优参数: 轮询间隔(poll_interval)建议 500ms-2s,批处理大小(batch_size)建议 50-200 条。retry_count 上限设为 5,超过后转入死信队列人工处理。在鉴权场景中,本地消息表适合异步同步角色变更事件,延迟控制在 3s 以内。

2.7 事务消息(RocketMQ 实现)

事务消息是消息队列对分布式事务的原生支持,以 RocketMQ 为例,核心流程如下:

  1. 发送半消息: 生产者发送半消息(half message)到 MQ,消息暂不可见。
  2. 执行本地事务: 生产者执行本地业务逻辑。
  3. 提交/回滚: 根据本地事务结果,向 MQ 发送 commit 或 rollback 指令。commit 后消息对消费者可见。
  4. 回查机制: 若 MQ 长时间未收到 commit/rollback,主动回查生产者的事务状态。
RocketMQ 事务消息流程 生产者 MQ Broker 消费者 半消息 commit/rollback 投递消息 回查事务状态 回查间隔默认 60s,可调小至 6s 以降低延迟 回查次数上限默认 15 次,超限后消息进入死信队列
鉴权场景最佳实践: 事务消息适合“凭证下发 + 审计日志”的原子性场景。半消息阶段不暴露凭证,commit 后消费者才收到完整凭证。回查接口必须幂等且快速(< 100ms),避免阻塞 MQ 回查线程池。

2.8 方案选型决策树

在 development 环境的鉴权拦截调优中,方案选型需考虑一致性要求、并发量、团队维护成本。以下决策树可作为参考:

  • 需要强一致性? → 2PC(低并发)或 3PC(中并发)
  • 允许最终一致性?
    • 业务可补偿 → TCC(高并发)或 Saga(长链路)
    • 业务不可补偿 → 事务消息(MQ 原生支持)
    • 无 MQ 基础设施 → 本地消息表(轻量实现)