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 在 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 的补偿事务可能被多次调用(网络重试、协调器重启)。必须在补偿逻辑中实现幂等控制,例如使用唯一请求 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 为例,核心流程如下:
- 发送半消息: 生产者发送半消息(half message)到 MQ,消息暂不可见。
- 执行本地事务: 生产者执行本地业务逻辑。
- 提交/回滚: 根据本地事务结果,向 MQ 发送 commit 或 rollback 指令。commit 后消息对消费者可见。
- 回查机制: 若 MQ 长时间未收到 commit/rollback,主动回查生产者的事务状态。
鉴权场景最佳实践: 事务消息适合“凭证下发 + 审计日志”的原子性场景。半消息阶段不暴露凭证,commit 后消费者才收到完整凭证。回查接口必须幂等且快速(< 100ms),避免阻塞 MQ 回查线程池。
2.8 方案选型决策树
在 development 环境的鉴权拦截调优中,方案选型需考虑一致性要求、并发量、团队维护成本。以下决策树可作为参考:
- 需要强一致性? → 2PC(低并发)或 3PC(中并发)
- 允许最终一致性?
- 业务可补偿 → TCC(高并发)或 Saga(长链路)
- 业务不可补偿 → 事务消息(MQ 原生支持)
- 无 MQ 基础设施 → 本地消息表(轻量实现)