之前总结了TiDB的架构以及TiKV、TiFlash的内部细节,本文着重说明TiDB的事务处理能力,事务处理是数据库满足OLTP特性的关键技术。
TiDB的事务隔离级别支持快照隔离(SI)和可重复读(RR)。SI允许事务中每个请求读取到数据的一致版本。事务中不同语句对同一个键可能会读取到不同的值(比如RC隔离级别),RR将保证事务中对相同的键将始终读取相同的值。TiDB同时实现了多版本并发控制(MVCC),避免了读写锁定并防止写写冲突。
TiDB中一个事务会涉及到SQL引擎、TiKV和PD之间的协同工作,其中:
(1)SQL引擎:负责协调事务。接收客户端SQL请求,将数据转换为KV格式,并使用两阶段提交(2PC)将事务写入TiKV。
(2)PD:负责管理逻辑Regions及物理位置;提供全局严格递增的时间戳。
(3)TiKV:提供分布式事务接口,实现MVCC,并将数据持久化到磁盘。
TiDB既实现了乐观锁,也支持悲观锁。锁的实现来自于Percolator模型,该模型选择一个键作为主键,并用它来表示事务的状态,并使用基本的两阶段提交来执行事务。
![]() |
乐观事务处理流程为:
(1)当收到客户端的begin命令后,SQL引擎向PD请求一个时间戳作为事务的开始时间戳start_ts。
(2)SQL引擎从TiKV读取数据并写入本地内存来执行DML。TiKV在事务的start_ts之前提供最近的提交时间戳commit_ts。
(3)当SQL引擎从客户端接收到commit命令时,它启动2PC协议。它随机选择一个主键,并行锁定所有键,并向TiKV节点发送预写。
(4)如果所有预写成功,SQL引擎向PD请求事务的commit_ts,并向TiKV发送命令。TiKV提交主键并向SQL引擎发送成功响应。
(5)SQL引擎将成功返回给客户端。
(6)SQL引擎通过向TiKV发送进一步的提交命令,以异步和并行的方式提交辅助键并清除锁。
对比悲观锁与乐观锁,其最大的区别在于何时获取锁。乐观事务中,锁是在预写阶段增量获取的;而悲观事务中,锁是在预写之前执行DML时获取。
在悲观事务中锁定键时,SQL引擎获取一个新的时间戳for_update_ts。如果SQL引擎无法获取锁,它可以重试从该锁开始的事务,而不是回滚并重试整个事务。在读取时,TiKV使用for_update_ts而不是start_ts来决定可以读取键的哪些值。通过这种方式悲观事务保持RR隔离级别。悲观事务还允许使用读提交(RC)隔离级别,这样可以减少事务之间的冲突,从而提高性能。
时间戳是由PD分配的,每个时间戳包括物理时间和逻辑时间。物理时间为当前时间,精度为毫秒级别;逻辑时间为18 bit。理论上,PD每毫秒可以分配2^18个时间戳。为降低延迟,客户端按批次从PD申请时间戳。





