写点什么

TIKV 分布式事务简介

  • 2024-04-12
    北京
  • 本文字数:3064 字

    阅读完需:约 10 分钟

作者: Lystorm 原文来源:https://tidb.net/blog/a6dc6e8b


借着这次社区 PCTA/PCTP/PCSD 免费考证的活动,看了不少的教程与优秀的社区文章,选了其中的一个点展开总结一下,也希望可以写成文章让大家给看看我这块的理解是不是存在偏差。


分布式事务是事务的一种,指的是事务的参与者,支持事务的服务器,参与事务的资源服务器,事务的管理者等角色分别位于不同的节点上。


 通常我们用 ACID 来定义事务(ACID 概念定义)。简单说一下这些概念以及 TiDB 数据库是怎么实现 ACID 的:


 A(原子性):构成事务的所有操作,要么全部执行成功,要么全部执行失败,不会出现部分成功或者部分失败的情况,TiKV 基于 GOOGLE 的 Percolator 论文实现原子性,核心思想是通过使用 Primary Key 所在 region 的原子性来保证。


C(一致性):在事务执行前后,数据库的一致性约束没有被破坏,比如,小李去银行取 100 块钱,取之前是 500,取之后应该是 400,取之前和取之后的数据为正确数值就为一致性,如果取出 100,而银行里面的钱没有减少,就没有达到一致性的要求。TiKV 在写入数据之前,会对数据的一致性进行校验(Raft 算法),校验通过才会写入内存并返回成功。


I(隔离性):隔离性主要用于处理并发场景,TiDB 目前只支持一种隔离级别 快照隔离 (Snapshot Isolation, SI) ,又称其为“可重复读”,即在事务内可重复读,只能读到该事务启动时已经提交的其他事务修改的数据,未提交的数据或在事务启动后其他事务提交的数据是不可见的 (通过 MVCC 实现,本段提到的隔离级别会在后续 MVCC 机制的介绍中展开介绍,此处先暂做了解)。


D(持久性):事务一旦提交成功,数据全部持久化到 TiKV, 此时即使 TiDB 服务器宕机也不会出现数据丢失。


首先看一下下面这张我根据自己的理解梳理出来的一个简易的流程图:



整体的流程可以在上图左侧看到,TIDB Server 收到请求后,会向 PD 发起 TSO 请求(TSO 是一个单调递增的时间戳,由 PD leader 分配。TiDB 在事务开始时会获取 TSO 作为 start_ts、提交时获取 TSO 作为 commit_ts,依靠 TSO 实现事务的 MVCC),申请一个开始时间戳 start_ts,TIDB Server 会在内存中先修改数据,当数据要进行持久化时,开始进行两阶段提交,两阶段提交包括了 prewrite 和 commit 两个步骤,commit 完成后,事务才真正的完成了事务的持久化。


流程内部涉及到的逻辑较为抽象,因此本文就对一个实际的事务实现流程进行说明,以便于理解。


一个 TIKV 集群,Node0 为 leader 节点,Node1 与 Node2 为 follow 节点,两个 follow 节点的逻辑完全相同,因画布限制,不对 Node2 进行详细介绍,仅介绍 Node1 与 Node0。目前集群中已经存在了两条数据,分别为<1,'小一'>和<2,'小二'>。


TIKV 的数据最终在 RocksDB 底层存储的数据格式如上图所示,可以看到是存储在三个 CF 中,Lock CF 存储锁信息,Default 存储数据的 key,start_ts,value 信息,Write CF 存储数据的 key,commit_ts,start_ts 信息。接下来会在对事务流程的介绍过程中,详细说明这些信息。


 现在 TIKV 接到了事务请求,需要执行以下操作:


①将<1,'小一'>更新为<1,‘小李’>


②新增<3,'小张'>


接到请求后,TIDB Server 会向 PD 发起 TSO 请求,申请一个开始时间戳 start_ts=110,同时会在内存中修改数据,修改‘小一’为 ’ 小李,新增<3,'小张'>,此时所有的操作、数据都还停留在内存中,事务是要进行持久化的,开始进行持久化时,就进入了 2 阶段提交步骤。


prewrite 阶段,会将内存中修改的数据,全部都写入到磁盘中,并且给数据上锁。


leader 节点上 Lock CF 和 Default CF 中分别会写入两条信息,第一条数据:Lock CF 中的<1,(w,pk,110..)>,1 代表 key=1,w 代表 write 操作,pk 代表这是一个主锁,prewrite 阶段,会将写请求的第一行数据作为主锁(primary key),110 表示 start_ts,“..“ 中的内容表示锁的其他信息,如校验信息,指针信息等,不影响整体逻辑理解,所以暂不做展开,下文也不再做解释。Default CF 中的 put<1\_110,'小李'>,put 代表数据写入,1_110 代表 key=1,事务的 start_ts=110,‘小李’是 value。第二条数据:Lock CF 中的<3,(w,@1,110..)>,3 代表 key=3,w 代表 write 操作,@1 代表这是一个副锁,prewrite 阶段,会将写请求除了第一行数据之外的行作为副锁(secondary key),110 表示 start_ts。Default CF 中的 put<3\_110,'小张'>,put 代表数据写入,3_110 代表 key=3,事务的 start_ts=110,‘小张’是 value。


follow 节点上的 Lock CF 和 Default CF 中也会写入两条信息,第一条数据:Lock CF 中的<1,(w,@1,110..)>,1 代表 key=1,w 代表 write 操作,@1 代表这是一个副锁,110 表示 start_ts,。Default CF 中的 put<1\_110,'小李'>,put 代表数据写入,1_110 代表 key=1,事务的 start_ts=110,‘小李’是 value。第二条数据:Lock CF 中的<3,(w,@1,110..)>,3 代表 key=3,w 代表 write 操作,@1 代表这是一个副锁,110 表示 start_ts。Default CF 中的 put<3\_110,'小张'>,put 代表数据写入,3_110 代表 key=3,事务的 start_ts=110,‘小张’是 value。


写入 Lock CF 就是对数据进行上锁的过程,上锁前,会校验是否有别的客户端对数据已经进行上锁,且 primary key 和 secondary key 是一前一后进行的,prewrite 阶段会等待所有节点返回成功后才会进行下一步的 commit,如果这中间有任何一步或者任何节点失败了,那么 prewrite 就直接失败,所有节点直接触发回滚(Rollback)。


commit 阶段,TIKV 会向 PD 获取 commit_ts,PD 会确保 commit_ts 的值大于 start_ts, 本示例中 commit_ts=120,获取 commit_ts 后,将数据写入 Write CF 中,并将 Lock CF 中的锁删除。


leader 节点上的 Write CF 和 Lock CF 会写入两条数据,第一条数据:Write CF 中的 put<1\_120,110>,put 代表数据写入,1_120 代表 key=1,事务的 commit_ts=120,110 代表 start_ts=110。Lock CF 中的<1,(D,pk,120..)>,1 代表 key=1,D 代表 delete 锁操作,pk 代表这是一个主锁,120 表示 commit_ts。第二条数据:Write CF 中的 put<3\_120,110>,put 代表数据写入,3_120 代表 key=3,事务的 commit_ts=120,110 代表 start_ts=110。Lock CF 中的<3,(D,@1,120..)>,3 代表 key=1,D 代表 delete 锁操作,@1 代表这是一个副锁,120 表示 commit_ts。


follow 节点上的 Write CF 和 Lock CF 也会写入两条数据,第一条数据:Write CF 中的 put<1\_120,110>,put 代表数据写入,1_120 代表 key=1,事务的 commit_ts=120,110 代表 start_ts=110。Lock CF 中的<1,(D,@1,120..)>,1 代表 key=1,D 代表 delete 锁操作,@1 代表这是一个副锁,120 表示 commit_ts。第二条数据:Write CF 中的 put<3\_120,110>,put 代表数据写入,3_120 代表 key=3,事务的 commit_ts=120,110 代表 start_ts=110。Lock CF 中的<3,(D,@1,120..)>,3 代表 key=1,D 代表 delete 锁操作,@1 代表这是一个副锁,120 表示 commit_ts。


数据在读取时,会寻找 Lock CF 和 Write CF 中是否存在复合条件的数据,若在 Write CF 中存在复合条件的值,则从 Write CF 中取出 key 和 start_ts 的值,去 Default CF 中获取这个 key 对应的 value 值。因此 Write CF 中存储的是事务的 key+commit_ts 和 start_ts 的值。Lock CF 与 Write CF 读取的逻辑,这块的内容会在 MVCC 的文章中展开说明,此时暂时先做个了解。


commit 阶段也存在回滚机制,与 prewrite 不同的事,commit 阶段只有当 primary key 的事务提交失败了,才会触发回滚。至此 TIKV 的分布式事务介绍完毕。


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

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

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

评论

发布
暂无评论
TIKV分布式事务简介_TiKV 底层架构_TiDB 社区干货传送门_InfoQ写作社区