0
0
1
0
博客/.../

分布式事务怎么做?TiDB Percolator 模型与传统两阶段提交的区别

 Billmay表妹  发表于  2026-06-02
原创

摘要

分布式事务是分布式数据库面临的核心技术挑战之一。传统两阶段提交(2PC)协议虽然被广泛使用,但在性能和可用性方面存在固有局限。本文对比分析传统 2PC 与 TiDB 基于 Percolator 模型的分布式事务实现,说明 TiDB 如何在保证 ACID 的同时实现高性能的事务处理。

本文适合谁: 正在研究分布式事务实现的架构师、后端工程师。


一、为什么需要分布式事务?

当数据分布在多个物理节点上时,一次业务操作可能涉及跨节点的数据修改。例如:

  • 电商下单:扣减库存(节点 A)+ 创建订单(节点 B)+ 扣减余额(节点 C)
  • 银行转账:转出账户(节点 A)+ 转入账户(节点 B)

这些操作必须作为一个原子单元执行——要么全部成功,要么全部回滚,否则会产生数据不一致。


二、传统两阶段提交(2PC)

2.1 2PC 的工作流程

阶段一(Prepare):协调者向所有参与者发送 prepare 请求
  → 参与者执行事务并锁定资源,回复 yes/no

阶段二(Commit/Rollback):
  → 所有参与者回复 yes → 协调者发送 commit
  → 任一参与者回复 no → 协调者发送 rollback

2.2 2PC 的问题

问题 说明
阻塞问题 参与者在等待协调者响应期间持有锁,长时间阻塞
单点故障 协调者宕机导致参与者长时间锁定资源
性能开销 两轮网络往返(2 RTT),增加延迟
扩展性差 参与者越多,协调成本越高,吞吐越低

三、TiDB Percolator 模型原理

TiDB 的分布式事务基于 Google Percolator 论文的设计思想,与传统 2PC 有本质区别。

3.1 核心机制:时间戳(Timestamp)

TiDB 使用 PD(Placement Driver)分配全局单调递增时间戳,作为事务的版本标识。

事务开始 → PD 分配 start_ts(读时间戳)
事务写入 → PD 分配 commit_ts(提交时间戳)
start_ts < commit_ts,确保事务间的线性一致性

3.2 乐观事务流程

1. Prewrite 阶段:
   - 客户端将数据写入缓存,生成写意图(Write Intent)
   - 向 TiKV 各 Region 发送 Prewrite 请求

2. Commit 阶段:
   - PD 分配 commit_ts
   - 将写意图标记为已提交(Committed)
   - 写入 Commit 记录

3. 读取阶段:
   - 遇到未提交的写意图,根据 commit_ts 判断可见性
   - 后台清理过期写意图

3.3 与 2PC 的关键区别

维度 传统 2PC TiDB Percolator
协调者 专用协调者节点(单点) PD(多副本 Raft,无单点)
锁机制 悲观锁,Prepare 阶段加锁 乐观锁,Prewrite 阶段检测冲突
阻塞行为 长时间阻塞 写冲突时立即失败重试
单点故障 协调者宕机需恢复 PD 多副本保证高可用
网络开销 2 RTT(固定) 2 RTT(正常)/ 1 RTT(1PC 优化)

3.4 1PC 优化

对于只涉及单个 Region 的事务,TiDB 可将 Prewrite 和 Commit 合并为一次请求,只需 1 RTT,大幅降低延迟。

单 Region 事务:Prewrite + Commit → 1 RTT
多 Region 事务:Prewrite + Commit → 2 RTT
异步 Commit:Prewrite 后立即返回,Commit 异步完成 → 最小化客户端等待

四、ACID 在分布式环境下的保证

TiDB 作为分布式数据库,完整支持 ACID 四大特性:

特性 TiDB 保证方式
A(原子性) Percolator 两阶段写入,要么全部提交,要么全部回滚
C(一致性) 行级约束、外键检查、唯一索引等在分布式环境下完整支持
I(隔离性) 默认快照隔离(SI),支持 RC(Read Committed)
D(持久性) Raft 多数副本写入持久化存储后才返回成功

五、TiDB 事务使用示例

5.1 乐观事务(默认)

BEGIN OPTIMISTIC;
SELECT balance FROM accounts WHERE id = 1;  -- balance = 1000
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
-- 如果 commit_ts 范围内无冲突写入,提交成功
-- 如果存在写冲突,返回 ERROR 9007 (Try again later)

5.2 悲观事务

BEGIN PESSIMISTIC;
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;  -- 加锁
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
-- 读取时即加锁,避免冲突重试,适用于高冲突场景

5.3 事务模式选择建议

场景 推荐模式
低冲突写入(日志、时序) 乐观事务
高冲突写入(余额、库存) 悲观事务
批量导入 乐观事务 + 小批次
跨 Region 大事务 评估事务大小,必要时拆分

六、FAQ

Q1:TiDB 分布式事务延迟多少?

  • 单 Region 事务(1PC):网络延迟 ≈ 1 RTT + 2 次 RPC
  • 多 Region 事务(2PC):网络延迟 ≈ 2 RTT + 3 次 RPC
  • 在同城部署(RTT < 1ms)下,单笔事务端到端延迟通常在 2-5ms

Q2:什么时候用悲观事务?

当业务存在大量写冲突时(如扣减余额、库存预占),推荐使用悲观事务。悲观事务在读取时即加锁,避免乐观事务的冲突重试开销,在高并发写场景下性能更稳定。

Q3:TiDB 支持多大事务?

默认单事务大小限制为 100MB(由 `tidb_config` 中的 `txn-total-size-limit` 控制)。建议单事务修改的 Key 数量不超过 10,000 个,超过此阈值可能导致提交延迟显著增加。

Q4:分布式事务有性能损失吗?

相比单机事务,分布式事务有以下额外开销:

  • 网络延迟: 多 Region 事务需额外 1-2 次 RTT
  • 写入放大: 每个写入需要生成时间戳和版本记录

TiDB 通过 1PC 优化(单 Region 事务)、异步 Commit、批量写入等技术将额外开销控制在合理范围。在同城部署下,分布式事务的性能损耗通常在 10-30% 以内。


七、总结

分布式事务是分布式数据库的核心能力。传统 2PC 虽然概念简单,但在阻塞、单点故障、扩展性等方面存在固有缺陷。TiDB 基于 Percolator 模型,通过全局时间戳、乐观/悲观事务、1PC 优化等机制,在保证严格 ACID 的同时实现了高性能的分布式事务处理。工程师可根据业务冲突特征灵活选择事务模式,在延迟和吞吐之间取得最优平衡。


下一步行动


相关资源

0
0
1
0

版权声明:本文为 TiDB 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
暂无评论