关于mvcc的探索与研究
之前没有仔细研究过mysql的mvcc的具体实现,网上的大多数资料都是在讲readView在当前读和快照读的情况下,mysql读取的规则与逻辑,只讲了在一个是事务中,mysql如何实现可重复读的
没有说明mysql在并发事务中,更新时,会如何处理并发的数据,并且最后怎么样保证数据都能得到正确的更新
我自己写了一个demo,研究测试了下,具体流程如下:
模拟并发测试mvvc场景执行SQL如下:
有表test0105 ,有字段id,c ,c=1
事务A SQL:
set @@autocommit=0;
SELECT @@tx_isolation;
START transaction ;
SELECT * from test0105 WHERE id ='1';-- 1
SELECT SLEEP(2);
SELECT * from test0105 WHERE id ='1'; -- 1
update test0105 set c=c+1 where id ='1';-- 2
SELECT * from test0105 WHERE id ='1'; -- 2
SELECT SLEEP(4);
SELECT * from test0105 WHERE id ='1'; -- 2
COMMIT;
SELECT * from test0105 WHERE id ='1'; -- 2
事务B SQL:
set @@autocommit=0;
SELECT @@tx_isolation;
START transaction ;
SELECT c from test0105 WHERE id ='1';-- 1
SELECT SLEEP(2);
SELECT * from test0105 WHERE id ='1'; -- 1
update test0105 set c=c+2 where id ='1';
SELECT * from test0105 WHERE id ='1'; -- 4
SELECT SLEEP(4);
SELECT * from test0105 WHERE id ='1'; -- 4
COMMIT;
逻辑图
c=1 | ||||
事务A | 事务B | |||
开启事务 | ||||
开启事务 | ||||
快照读:c=1 | ||||
更新记录为+1 | ||||
快照读:c=2 | 执行快照读:c=1 | |||
更新记录 +2 | ||||
执行快照读:c=4 | ||||
执行快照读:c=2 | ||||
提交事务 | ||||
执行快照读:c=2 | ||||
执行快照读:c=4 | ||||
提交事务 | ||||
执行快照读:c=4 | ||||
最后结果 | c=4 |
结论如下:
在可重复读隔离级别下进行:
1.事务A进行更新字段c,会在undolog中,先增加排他锁,然后复制一行数据,作为roll back数据,然后将新的记录指向 旧的记录
2.事务B在事务A提交之前开启事务,此时继续做更新操作,也会在indolog 中 先增加排他锁,然后复制一行数据,作为roll back数据,然后将新的记录 指向 旧的记录
3.因为更新操作属于当前读,所以查到的是最新的数据,此时如果事务A在事务B 之前完成了更新操作,那么undolog中的链条记录如下
链条顶部 最新的记录 c=4
B生成的历史记录 c=2
A生成的历史记录 c=1
为什么在事务B更新后C会等于4,因为事务B进行的更新操作,属于当前读,会从undolog 顶部 获取最新的记录,此时为c=2,所以会在记录上增加2,此时即使没有提交事务A,也能读取到最新的记录 (我感觉是这个原因,或许是其他的= =)
最后事务A提交时 ,c=2 这是因为此时事务A读取的仍然是当前事务中的快照读,不会获取其他事务的快照读
最后事务B 提交时,c=4 因为事务B后于事务A提交,此时的值是最新的,就是4,此处可能是readview 的判断机制被重新触发了,不再读取第一次获取的快照读了
以上内容仅供参考,也欢迎大佬们帮忙多多斧正,接触mysql的时间太短了,好多东西不明白,正在努力搬砖中。。。。。