写点什么

TiKV 事务介绍

  • 2024-08-09
    北京
  • 本文字数:1495 字

    阅读完需:约 5 分钟

作者: TiDBer_jYQINSnf 原文来源:https://tidb.net/blog/2bbb68c6


这一篇介绍 tikv 怎么实现的跨节点事务。内容参考了不少 TiDB 的文档和视频资料,这里也算是消化以后的一次总结,希望能对大家理解 tikv 的事务有所帮助。

事务原理介绍

TiKV 采用了 Google Percolator 这篇论文中所述的事务模型,事务主要由客户端驱动,tikv server 端实现了 prewrite 、 commit、rollback 等接口. 在客户端调用 client.Commit() 时,开始进入分布式事务 prewrite 和 commit 流程, 客户端通过把要修改的 key 分组,发送给对应的 tikv-server, 调用不同的 tikv-server 接口实现事务逻辑。


TiKV 的主要事务逻辑如图:



这里注意:重点在 commit 阶段,tikv-client 首先向持有 primary key 锁的 tikv-server 发送 grpc 请求,当 primary key 锁被提交后才可以并行发送后续的锁的提交请求。把分布式事务的成功和失败的决策点缩小到单一一个节点上。


下图是 TiDB 事务 2 阶段提交的过程,这里假设是把<1,Jack>,<2,Candy>两个属于不同 region、不同 tikv 的 key 按事务写入 tikv。



TIKV 底层是 rocksdb,rocksdb 支持多个 column family 原子性的写入一组 key。tikv 正是依赖这一特性,利用 3 个 column family 实现 percolator 论文所述的事务模型。


上图中同一种颜色标识的两个 column family 中的 key 代表一次原子写入,要么都成功,要么都失败。


1. 首先在所有行的写操作中选出一个作为 primary row,其他的为 secondary rows


2. 然后是 prewrite,在 default cf 和 lock cf 写入数据,把要写入的变更按 region 分成 2 个 grpc,分别发送给 2 个 tikv。


3. 两个 tikv 分别在 default cf 中写入数据,lock cf 中写入锁。其中 tikv1 中的是主锁,tikv2 的是从锁。上锁前会检查:该行是否已经有别的客户端已经上锁 (Locking) 是否在本次事务开始时间之后,检查 versions ,是否有更新 [startTs, +Inf) 的写操作已经提交 (Conflict) 在这两种种情况下会返回事务冲突。否则,就成功上锁。将行的内容写入 row 中,版本设置为 startTs。


4. 提交时,tikv_client 首先给主锁所在的 region 发送 grpc,在 write cf 中写入 commit 记录 put<1\_100,100>, 删除 lock cf 中的锁<1,(D,pk,1,100..)>, 这两个 cf 的数据利用 rocksdb 支持的多 cf 的原子写入保证同时成功或失败。


5. 主锁提交成功后才给 tikv2 发送 grpc,tikv2 在 write cf 中写入 commit 记录 put<2\_110,100>,删除 lock cf 中的锁<2,(D,@1, 2,100...)>。 同样也是原子写入。


防止死锁




为了防止一个事务的 key prewrite 后,事务还没提交,tikv-client 异常退出后,事务残留后,其他 tikv-client 无法读取到这个 key,tikv 给 prewrite 设置了 ttl,当 ttl 过期后可以执行清除锁。 如果 ttl 时间太短,又会导致大的事务还没提交就锁失效,如果 ttl 时间太长,又会导致清理锁的时间太长,导致某一个 key 长时间无法读取。


因此 tikv-server 中引入了 txn_heartbeat 接口。每次 prewrite,根据 prewrite 的数据的大小不同,给锁设置的超时时间从 3 秒到 120 秒不等。同时在 prewrite 以后,开启一个 goroutine 不断刷新 ttl,直到 ttl 达到整个事务的最大提交时间或者事务提交。

事务中的读操作:

1. 检查该行是否有 Lock 标记,如果有,表示目前有其他事务正占用此行,如果这个锁已经超时则尝试清除,否则等待超时或者其他事务主动解锁。注意此时不能直接返回老版本的数据,否则会发生幻读的问题。


2. 读取至 startTs 时该行最新的数据,方法是:读取 write cf ,找出时间戳为 [0, startTs], 获取最大的时间戳 t,然后读取为于 t 版本的数据内容。


大致的事务提交流程介绍到这。


发布于: 刚刚阅读数: 4
用户头像

TiDB 社区官网:https://tidb.net/ 2021-12-15 加入

TiDB 社区干货传送门是由 TiDB 社区中布道师组委会自发组织的 TiDB 社区优质内容对外宣布的栏目,旨在加深 TiDBer 之间的交流和学习。一起构建有爱、互助、共创共建的 TiDB 社区 https://tidb.net/

评论

发布
暂无评论
TiKV 事务介绍_TiKV 源码解读_TiDB 社区干货传送门_InfoQ写作社区