TiDB的分布式事务本质上是基于Percolator模型实现的两阶段提交,而底层数据复制和一致性保障则完全依赖Raft共识协议。简单说,TiDB把事务分成"预写"和"提交"两个阶段,每个数据Region通过Raft协议在多个节点间复制日志,确保任何时刻数据都有多数派确认,从而在节点故障时自动切换、不丢数据。这套机制让TiDB同时具备了强一致性和高可用性,是它区别于很多NewSQL数据库的核心竞争力。

TiDB分布式事务的核心:Percolator模型

TiDB没有采用传统数据库的两阶段提交(2PC)加锁方案,而是选择了Google Percolator论文中提出的乐观事务模型。这个模型的核心思想是:先写数据但不立即提交,等所有写操作都成功了再统一提交。具体流程分为三步:Prewrite(预写)、Commit(提交)、Cleanup(清理)。

在Prewrite阶段,TiDB会对涉及的每一行数据加一个Primary Lock(主锁),同时写入一个带有时间戳的临时值。这个时间戳由TiDB的全局事务管理器(TSO)分配,保证全局唯一且单调递增。如果某一行数据已经被其他事务锁定,当前事务就会进入等待或者直接回滚。

在Commit阶段,TiDB会把Prewrite阶段写入的临时值正式变成可见值,同时清除Primary Lock。这个阶段如果失败,事务会被回滚,已写入的临时值会被清理掉。Cleanup阶段则是异步地清理残留的锁信息,防止长时间占用资源。

整个过程的关键在于时间戳。TiDB用时间戳来判断事务的先后顺序,用"先写者胜"的策略解决冲突。这种设计避免了传统2PC中协调者单点故障的问题,因为TiDB的PD(Placement Driver)组件只负责调度和元数据管理,不直接参与事务提交决策。

Raft共识协议在TiDB中的具体作用

TiDB把数据切分成很多Region(默认96MB一个),每个Region有多个副本(默认3个),这些副本之间通过Raft协议保持数据一致。Raft是一种强一致性共识算法,它把节点分为Leader、Follower和Candidate三种角色。任何写操作都必须经过Leader节点,Leader把操作写入本地日志后,复制给多数Follower,等多数Follower确认后才算提交成功。

Raft在TiDB中的工作流程非常清晰:客户端发来写请求,TiKV层找到对应Region的Leader,Leader将写操作封装成Raft Log Entry,通过AppendEntries RPC发给所有Follower。当超过半数节点(比如3副本中的2个)确认收到日志后,Leader就把这条日志标记为Committed,然后应用到状态机(也就是实际的KV存储引擎)中。最后Leader通知客户端写入成功。

如果Leader节点宕机了怎么办?剩下的Follower会在选举超时后发起新一轮选举,选出新的Leader继续服务。因为Raft保证任何时刻最多只有一个Leader,而且新Leader一定包含所有已提交的日志,所以不会出现数据丢失或者脑裂的情况。TiDB的PD组件会监控各个Region的Leader分布,必要时进行调度平衡,避免某个节点过载。

分布式事务与Raft如何协同工作

很多人会问:Percolator事务模型和Raft协议之间是什么关系?答案是它们在不同层次各司其职。Percolator解决的是"跨行跨表的事务一致性"问题,Raft解决的是"单Region内数据副本一致性"问题。一个完整的TiDB写事务,实际上是先在TiDB层完成Prewrite和Commit的逻辑判断,然后把最终的写入操作下沉到TiKV层,由TiKV通过Raft协议在多副本间复制。

举个具体例子:假设你执行一条UPDATE语句修改了两行不同Region的数据。TiDB的事务协调器会先给这两行分别做Prewrite,都成功后再统一做Commit。每个Region的Commit操作在TiKV层就是一条Raft日志,需要多数副本确认。如果其中一个Region的Raft提交失败,整个事务就会回滚,已经Prewrite的另一个Region也会被清理。这就是两层机制协同保证原子性的方式。

TiDB事务的隔离级别与MVCC实现

TiDB默认使用快照隔离(Snapshot Isolation)级别,这是通过多版本并发控制(MVCC)实现的。每个写入操作都会生成一个新版本,版本号就是事务的提交时间戳。读取操作会根据自己的时间戳选择合适的版本:如果某个版本的提交时间戳小于等于当前事务的开始时间戳,就可以读;否则就需要等待或者读更早的版本。

这种设计让读写互不阻塞,读操作不需要加锁,写操作也不会被读操作挡住。TiDB还支持可重复读(Repeatable Read)级别,通过在事务开始时记录一个快照时间戳,后续所有读操作都基于这个快照,保证同一个事务内多次读取结果一致。

-- TiDB中查看事务信息的示例
SELECT * FROM information_schema.tidb_trx;

-- 查看当前活跃的锁信息
SELECT * FROM information_schema.tidb_locks;

-- 查看Raft相关的Region状态
SELECT * FROM information_schema.tikv_region_status;

Raft日志复制的性能优化细节

TiDB在Raft层面做了不少工程优化。首先是批量提交(Batch Commit),Leader不会每来一条写请求就发一次RPC,而是攒一批一起发,减少网络往返次数。其次是Pipeline机制,Leader可以在没收到上一批确认的情况下继续发送下一批日志,提高吞吐量。另外TiDB还支持Learner角色,新加入的副本先以Learner身份同步数据,不参与投票,等追上进度后再转为正式Follower,避免影响写性能。

在网络层面,TiDB推荐同机房部署,因为Raft对网络延迟比较敏感。跨机房部署虽然支持,但会显著增加提交延迟,因为需要等远程Follower确认。TiDB 6.x之后引入了Follower Read和Witness节点等特性,进一步优化了读性能和跨地域部署的灵活性。

故障恢复与数据安全保障

当某个TiKV节点宕机,PD会检测到并触发Region补副本操作。Raft协议保证只要多数副本存活,服务就不中断。如果恰好挂掉的是Leader,剩余Follower会在几秒内选出新Leader。整个过程对上层应用透明,客户端只需要重试即可。

TiDB还有一个重要的安全机制叫"Safe Point",它确保在某个时间点之前的所有事务都已经提交或回滚,不会出现旧事务的数据被GC清理掉的情况。这在做数据备份和迁移时特别关键。配合BR(Backup & Restore)工具,可以在不影响线上服务的情况下做全量或增量备份。

与其他分布式数据库的对比优势

和CockroachDB相比,TiDB的事务模型更接近MySQL的使用习惯,对存量MySQL应用迁移更友好。CockroachDB用的是类似Spanner的TrueTime方案,依赖硬件时钟同步;TiDB用的是TSO加Percolator,不依赖特殊硬件。和OceanBase相比,TiDB的架构更轻量,组件解耦更彻底,运维复杂度相对低一些。

不过TiDB也有短板:Percolator模型在高冲突场景下会有大量事务回滚,性能下降明显;Raft协议在跨地域场景下延迟较高。这些都是选型时需要考虑的实际因素。

实际生产环境中的最佳实践

在生产环境部署TiDB,建议Region大小根据业务特点调整,热点表可以适当拆小。PD的调度策略要根据实际负载配置,避免频繁迁移。事务尽量控制在合理范围内,避免单个事务涉及太多行,减少Prewrite阶段的锁冲突。监控方面要重点关注Raft的Leader切换频率、事务冲突率和GC安全点延迟,这些指标直接反映集群健康状态。

总结来说,TiDB的分布式事务靠Percolator模型保证跨行原子性,Raft协议保证副本间强一致性,两者配合形成了一套完整的分布式数据保障体系。理解这两个核心机制,就掌握了TiDB最关键的技术内核。