背景
mvcc是一种同悲观锁和乐观锁的一种并发锁模式,用以解决mysql并发访问同一行数据因阻塞而过慢的问题。
mvcc
MVCCMVCC
,全称 Multi-Version Concurrency Control
,即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。
mysql在rr和rc隔离模式下,事务快照读访问一行数据,会生成一个readview,并在undo log日志里生成一条记录,且你快照读访问的那条记录的隐式字段也会修改
记录的隐式字段
db_trx_id:记录着上一个要对次修改的事务
db_row_id:记录该记录的隐藏主键
db_roll_ptr:记录着该记录要回滚的上一个版本的指针
undo log
undo log分为两种
insert undo log
update undo log
参考:mysql技术内幕 Innodb存储引擎第二版
如上图所示update undo log在insert undo log中做了一些补充,*代表对此字段进行了压缩
所以我先说insert undo log的字段
next记录着下一个undo log的位置,start记录着undo log的开始位置
type_cmpl记录此undo log的类型
undo_no记录着事务id(mvcc要素)
table_id记录着表id
剩余的n_unique_index指这行记录唯一键值
update undo log与insert undo log相同字段名的字段意义大都相同,
除了type_cmpl
trx_undo_del_mark_rec:标记删除操作
trx_undo_upd_del_rec:更新一个已被删除的记录
trx_undo_upd_exist_rec:普通更新
data_poll_ptr(回滚指针)记录上一个undo log记录位置(mvcc要素,用作回滚操作),其他的如下图,很多都方便不理解,估摸着要有机会在深入
参考:MySQL · 引擎特性 · InnoDB undo log_bohu83的博客-CSDN博客
readview
在一个事务开启后,并在一段时间后访问一条数据,会对该行数据执行快照读,生成针对与该行数据和该事务的一个readview,这是mvcc最后的一个实现要素
readview简单理解有三个属性:
一个是存放快照读执行时或者readview生成前一刻,当前这段数据所有的活跃事务的id列表,我们暂且称这个列表的名字是trx_list
一个是trx_list中最小的trx_id,up_limit_id
一个是快照读执行时或者readview生成前一刻,下一个尚未分配的事务id,low_limit_id
整体流程
mvcc主要原理就是将undo链表记录中的事务id与readview中的三个属性进行比较,判断事务可以看到哪一个版本的数据
参考:【MySQL笔记】正确的理解MySQL的MVCC及实现原理_SnailMann的博客-CSDN博客
事务1开始执行,修改完行数据并提交,并更改行数据的隐式字段trx_id
事务2开始执行,修改行数据,并在undo log里生成undo记录,记录事务2要修改的信息
事务3开始进行快照读,发现undo log存在这条行记录的最近修改,生成readview记录所有当前事务id(2),并取得最近提交的事务id,赋给up_limit_id,将下一个生成事务的id赋给low_limit_id,
事务4开始执行,修改完数据并提交
当事务3再次查询时,会把之前生成的readview与undo log里的记录作比较,判断可见性,
如果这一行undo记录的事务id>low_limit_id,代表着这一行的事务在readview生成时还未执行,这肯定就是不可见的,进行下一次判断,反之则再次判断id是否大于up_limit_id,如果小于,则直接判断的此条记录可见并返回,反之则判断trx_list是否包含此id,如果包含,代表这个事务在readview生成的时候还没有开始执行,所以是不可见的,反之则代表,readview生成的时候,它已经提交了,所以是可见的
关于判断可见性这一块,我整理了下 up_limit_id, low_limit_id, trx_list
1,大于lowid,则代表,在readview生成之前
2,小于lowid,大于upid,代表在readview生成后,分为两个可能
1,已经结束,是可见的,则它在trxlist中
2,未结束,是不可见的,则它就不再trx_list中
3,小于upid,代表此事务在readview生成前就已经提交了
4,例外,在事务重新对行数据进行当前读之后,会重新更新readview
个人瞎想
我感觉的有些拗口,,似乎是readview生成时行记录记录的trx_id,trx_id是行记录最近提交事务的id,但我觉得似乎没有用到,然后我再想了下,似乎是我们只有通过最新行数据的roll_ptr(回滚指针)来定位数据
其次,一开始我想不通的一个点,我猜更改事务是基于最新提交的数据在undolog生成记录的,而不是在undolog里的最新的不知道到提没提交,或者已经回滚的数据的基础上生成undolog记录的(这是我想不通的点),所以每次快照读事务读到的必是一个可见的,且不是其他事务不知道提没提交,或者回没回滚得的数据