从零开始学TiDB(4)TiKV 分布式事务
一.什么是分布式事务?
如下图,
此时有一个begin,把<3,xxx> 修改成 <3,Frank>,之后commit 。 在分布式事务中会有什么问题?
如果此时有两个TiKV实例,两条数据分别存储在不同的实例中。
首先会把 <1,a> 修改成 <1,aa>
修改成功之后 如果此时 另外一个TiKV实例不可用了,那么事务的原子性就被破坏了(即一部分持久化成功了,一部分失败了)。这就是分布式事务的问题。
二,一个事务在TiDB中的流程
1.首先从PD组件中获取TSO(事务开始时间),并且此时读取数据放到TiDB Server的内存中,在TiDB Server的内存中进行修改。
2.遇到commit,对事务进行持久化,开启两阶段提交
3.第一阶段提交,把在内存中修改的数据和锁信息给写入到TiKV节点中
4.第二阶段提交,去PD组件中得到事务结束时间
如下图:
三.事务如何在TiKV中存储
1.第一阶段提交
把在内存中修改的数据和锁信息给写入到TiKV节点中(prewrite 阶段写了两个列簇:Default 和Lock),TiKV节点会用三个列簇来存储(一类列簇对应一类键值对),在整个TiKV节点中有三个列簇,所对应的default 是修改的数据,lock 列簇记录锁信息,write 列簇对应提交信息。
即
- 向PD获取TSO
- 把数据写入default列簇,注意此时写入default列簇的数据 key的值不止是业务的id,而是id_TSO
- 写入Lock列簇 格式:<业务id,W(写锁),pk(主锁),业务id,TSO....> (所谓主锁就是分布式事务只会在事务的第一行加一把主锁
其它修改的行都指向这把主锁)
2.第二阶段提交
- 首先向PD获取事务结束时间
- 写入 write列簇 格式为:<业务id_事务结束时间,事务开始时间>
- Lock列簇中插入一行数据,告知锁被删除
注意事项:如果用户写入一行数据,该行数据长度小于255字节。那么会被存储在write列中,否则存到default列簇中。
所以write 列簇不单只有提交信息,还包括 数据小于255字节 修改的数据。
default列簇存储的都是超过255字节长度的数据。
四.TiDB如何解决分布式事务问题
如下图:
- 首先向PD节点申请一个事务开始时间,一个事务需要修改两行,发出commit指令后,TiKV node1 写default列簇 和Lock列簇(写主锁),TiKV node2写default列簇 和Lock列簇(@1的意思是指向业务id为1的锁)
- 向PD节点申请一个事务结束时间,第一行数据写入Write 列簇,写入Lock列簇(清理锁信息),新增一条 将id为1的写锁修改为D。
如果到了commit阶段,第一行数据持久化了,此时第二个节点宕机了
此时读不到TiKV node2节点的 锁释放信息和 提交信息,那么会根据Prewrite 写入的Lock信息去找主锁状况,如果检查到主锁已经被删除,那么系统就知道之前可能出现了宕机,则会根据主锁补全 TiKV node2 的LOCK列簇中信息和提交信息。
五,MVCC
如果一个事务还没有提交,TiDB 使用的是悲观锁
比如Transaction 2中 此时TiKV Lock列簇中存储了两把写锁,但Write 列簇中没有1_115和4_115的提交信息,这两把锁是可以被感知到的,此时不能修改id为1和id为4 的行
如果此时 TSO=120
此时读id=1,首先到Write列簇中 找最近一次提交 ,查到110是最近的,拼一下1_100,此时去default 列簇中读取到Jack。这时候就读到了TSO为100的信息。
此时如果要修改第一行,首先查找最近的提交信息1_110,再去Lock中查找锁信息,查到这行信息被锁了,此时无法修改。 写不阻塞读。
总结
读操作先从Write 列簇中读取最近一次提交信息,找到最近一次提交信息的事务开始时间,直接去Default中去读
写操作也是先从Write 列簇中读取最近一次提交信息,找到最近一次提交信息的事务开始时间,之后要去Lock列簇中查看写是否被阻塞。如果没阻塞才可以去Default列簇中去读
原文地址:https://blog.csdn.net/dnuiking/article/details/144404852
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!