mysql 中的 MVCC 原理流程
mysql 事务中的多版本并发控制,属于链式结构一般称为版本链,mysql 其中的2个隔离级别(读已提交,可重复读)就是根据mvcc实现的。
流程:
如果一张表中有字段 id=1 name =张三
-
当事务A 开始的时候,会在undo log 中加入一行数据,相当于快照,用来保存即将要修改的原始数据,然后事务A修改了某一行数据 比如id=1, 将name改成李四, 那么就会在undo log 中再生成一行数据,数据中是有2个隐式字段的,一个trx_id保存操作当前这条数据的事务id,一个roll_pointer保存上一个事务的回滚指针;
-
然后事务B也开始了,也同样操作id=1的数据,将name 改为王五 ,此时事务A 还没有结束,那undo log 中又会插入一行记录,其中roll_pointer 就会指向上一个undo log 的操作记录;
-
当又有一个查询事务过来,要读取 id=1 的那行数据,那到底会读取张三李四王五哪一个呢 ,这里就会有一个ReadView 读已提交,可重复读 这个2个之间的不同就是提现在这里;
-
readView 这个是视图用来保存当前正在活动的事务,就是begin了,但是没有commit的事务,通过这个判断事务对外可见,readView保存的事务id是一个集合,事务是按照时间顺序从小到大排列的,
- 当要访问的事务小于readView 中的最小id时,代表这个事务已经commit了 可以访问
- 当要访问的事务大于等于 readView中最小 并且小于等于readView 最大时,再判断要访问的事务是否存在其中,如果存在,不能访问,否则可以访问
- 当要访问的事务大于readView 中的最大值得时候,说明事务正正运行,还没来的急在readView 中提现,所以不让访问
-
这些记录都是从undo log 版本链中找,从最大的开始找,如果在readView 中存在那就继续找上一个版本,知道满足以上的可以访问的区间
-
如果是读已提交的,那每次都会获得一个新的ReadView,里面保存的事务id 都会更新,每次读取就可能不一样;如果是可重复读,每次读取都会按照第一个的ReadView 来判断,自会生成一个ReadView,所以每次读取的数据都是一样的;
-
所以读已提交,可重复读 是怎么实现,就是根据ReadView 的生成策略不同,导致ReadView 范围的不同实现的