数据库必知必会:TiDBTiKV分布式事务
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库必知必会:TiDBTiKV分布式事务相关的知识,希望对你有一定的参考价值。
(数据库必知必会:TiDB(4)TiKV分布式事务)
分布式事务
存在的问题
假设有这样一个事务:
begin:
update person set name = Tom where id = 1;
update person set name = Jack where id = 2;
commit;
由于数据在TiKV中是分布式存储的,那么这两条数据就有可能分布在TiKV的两个实例中,比如:id=1的记录在实例1中,id=2的记录在实例2中。当在事务执行过程中实例1上id=1的记录成功更新,而此时实例2出现故障导致id=2的记录更新失败,那么以上这个简单的事务的原子性就会遭到破坏。
Percolator事务模型
TiDB采用Google Percolator事务模型来解决分布式事务的问题。
事务的过程
假设有这样一个事务:
begin:
update person set name = Frank where id = 3;
commit;
事务开始
在事务开始时,begin时,TiDB会从PD中获取事务开始的时间戳TSO,假设TSO=100。
修改数据
然后TiDB将需要修改的数据读取到内存中,在内存中完成数据的修改。
TiDB在内存中修改数据的时候,不会将锁信息写入TiKV,此时其他会话无法感知锁的存在,是乐观事务。
事务提交
在commit的时候进入两阶段提交。
预写
在第一阶段,TiDB会写三个列族到TiKV中:
- Default列族:记录带有事务开始时间戳标记(100)的修改后的数据
- Lock列族:记录锁信息,在第一行数据加写锁(W),这是一把主锁(pk),并且记录下其他相关的信息
- Write列族:预留用来存放提交信息
此时,其他会话会感知到写锁的存在,其他会话不会进行id=3的数据的读、写操作。
提交
在第二阶段,TiDB会从PD中获取事务提交的时间戳TSO,假设TSO=110。
TiDB在Write列族中写入提交信息,包括事务提交的时间戳(110)和事务开始的时间戳(100),完成后在Lock列族中记录一条锁清理的数据,表示写锁已经被释放。
此时,其他会话可进行id=3的数据的读、写操作。
TiDB如何处理分布式事务
假设有这样一个事务:
begin:
update person set name = Jack where id = 1;
update person set name = Candy where id = 2;
commit;
并且两条数据分布在TiKV中的两个实例上。
TiDB会按照事务的处理过程进行处理。
在预写阶段,在事务中的第一条数据(节点1,id=1)上加主锁(pk),在节点2上记录的是附加锁(@1)表示的是主锁在id=1的那条记录那里。
此时,节点1和节点2上都有写锁,其他会话不能对这些数据进行读、写操作。
在提交阶段,节点1和节点2都写入提交信息和清理写锁。
当节点1提交成功,节点2提交失败,那么节点2上的写锁不会被清除。后续在读取数据的时候,发现有写锁存在,并且是附加锁(@1),此时需要判断事务是否提交,会根据附加锁的指向找到主锁,发现主锁已成功提交,则可判断自己在提交阶段出现了问题。因为主锁是成功提交的,所以附加锁这里只需要补充提交即可,继续写入提交信息,清理附加锁,Default列族中的数据变成最终数据。
MVCC
从前面的过程中知道,事务已预写、未提交的时候,数据不能进行读、写操作,这有一个问题,就是读也会被阻塞。
MVCC的引入是为了解决读操作被阻塞的,因为在修改中的、还未提交的数据,还不确定最后是否提交,那么读取修改前的数据应该是被允许的。
MVCC机制下,读操作按最近一次提交记录读取,无需关心锁信息,写操作需要先检查当前是否已存在其他写锁。
假设有两个事务:
--事务1
begin (start_ts = 100)
update person set name = Jack where id = 1;
update person set name = Candy where id = 2;
commit; (commit_ts = 110)
--事务2
begin (start_ts = 115)
update person set name = Tim where id = 1;
update person set name = Jerry where id = 4;
此时,事务1已成功提交,事务2未提交。
假设在时间戳TSO=120的时候,有会话读取数据:
select * from person where id in (1,2,4);
此时id=1和id=4的记录有锁信息但无提交信息,属于在事务中的数据,如果阻塞读操作,那么此时id=1和id=4的记录都是无法读取的。
引入MVCC后。
读id=1的数据时,从Write列族中发现最近一次提交是TSO=100,读操作可以读取TSO=100的数据。
写id=1的数据时,当前锁是TSO=115,因为TSO=115的锁还未提交,所以写被阻塞。
读id=2的数据时,从Write列族中发现最近一次提交是TSO=100,读操作可以读取TSO=100的数据。
写id=2的数据时,当前无锁,所以可以写。
读id=4的数据时,从Write列族中发现最近一次提交是TSO=80,读操作可以读取TSO=80的数据。
写id=4的数据时,当前锁是TSO=115的附加锁,因为主锁还未提交,所以附加锁也还在事务中,写被阻塞。
以上是关于数据库必知必会:TiDBTiKV分布式事务的主要内容,如果未能解决你的问题,请参考以下文章