MVCC
基本概念
- 系统版本号:每当启动一个事务时,系统版本号会递增。
- 事务版本号:事务开始时的系统版本号作为事务版本号,用于在select操作中与记录的DATA_TRX_ID字段做对比
- 记录的隐藏列
- DATA_TRX_ID:记录某行记录的系统版本号,每当事务commit对该行的修改操作时事务的版本号记录。
- DATA_ROLL_PTR:记录了此行记录的回滚记录指针,通过它召回历史版本。
- DELETE BIT:标记此记录是否有事务正在删除它最后真正删除是在事务commit之后。
增删改查中的MVCC操作
select:①执行select操作时,InnoDB会查找到对应的数据行,并对比DATA_TRX_ID(版本号),要求数据行的版本必须小于等于事务的版本,如果当前数据行版本大于此事务版本,那么InnoDB会进入undo log中查找。确保当前事务读取的是事务之前存在的,或者是由当前事务创建或修改的行。 ② InnoDB会查找到对应的数据行后,查看DELETE BIT是否被定义,只允许未定义,或者删除的版本要大于此事务版本号。保证在执行此事务之前还未被删除。 当且仅当这两个条件都成立才允许返回select结果!
insert: InnoDB创建新记录,并以当前系统的版本号为新增记录的DATA_TRX_ID,如果需要回滚则丢弃undo log。
delete: InnoDB寻找到需要删除的记录,将此记录的DELETE BIT设置为系统当前版本号,若事务回滚则去除DELETE BIT定义的版本号,若事务提交则删除行。
update: InnoDB寻找到需要更新的行记录,复制了一条新的记录,新记录的版本ID为当前系统版本号,新记录的回滚指针指向原记录,将原记录的删除ID也设置为当前系统版本号。提交后则删除原记录,若回滚则删除复制的记录,并清除原记录的删除ID。
锁相关
因此读操作可以分为
-
快照读:读取的是记录的可见版本,不加锁
-
当前读:读取记录的最新版本,并且对表上锁,确保不会有记录修改这条记录。
添加了关键字的特殊查询操作(for update),或者update、delete、insert都属于当前读,需要加锁。
这里的锁分为共享锁S和排他锁X。
记录锁:Record lock,给单条索引记录上锁,锁的是索引。
间隙锁:Gap lock, 它是存在于某一条记录和前一条或者后一条之间间隙的锁,它只要是用于解决RR隔离级别下的幻读问题。举个例子:在b和a,b和c之间加入了间隙锁,那么b的前后相邻的位置都不能插入记录。
RR隔离级别是如何解决幻读的?
通过gap锁,将可能重复的记录之间的间隙锁上,其他事务无法并发的往间隙中进行插入。通过X锁锁定索引,其他事务无法并发进行删除。通过读取快照,每次只能读取到在此事务之前的历史版本或此事务修改的数据,实现可重复读。