什么是MVCC
MVCC是全名多版本并发控制,是mysql用于处理并发事务下解决数据安全性的一种无锁机制。对应的是LBCC,基于锁的并发处理控制。
并发事务下存在的数据安全问题
- 脏读
- 不可重复读
- 幻读
脏读: 一个事务读取数据的过程中,读取到另一个事务未提交到数据,这种多半是事务隔离级别为读未提交
不可重复读:在一个事务当中,多次读取相同条件的数据,发现多次读取的数据有差异,不相同
幻读: 在一个事务当中, 多次读取相同条件读数据,发现前后数据条数不一样 (另一个事务插入一条数据,后发生数据回滚)MVCC的实现原理
MVCC的实现, 依赖隐式字段, undo.log日志, 版本链, 快照读和当前读, 读视图这个几个核心要素。 - 隐式字段:每一行数据记录会有额外两个固定字段 DB_TRX_ID ,表示当前事务号,事务号递增,用于判断事务执行顺序。 DB_ROLL_PTR 回滚指针,用于指向上一个版本的undo.log记录,存储与回滚段当中, DB_ROW_ID 隐含的自增ID,如果记录当中有主键或者有非空的唯一键,则不需要生成自增ID
- undo.log: 事务开始之前,会记录更新数据的反向操作记录,备份起来,用于事务回滚
- 版本链:多个事务操作同一条记录,会产生多个版本的undo.log,这些日志通过回滚指针的指向,形成一个链表,俗称版本链
- 快照读和当前读
快照读:就是不加锁的读取,不阻塞
当前读:锁定读,读取的记录是最新版本,同时需要现货去对应记录的锁SELECT * FROM student LOCK IN SHARE MODE; # 共享锁 SELECT * FROM student FOR UPDATE; # 排他锁 INSERT INTO student values ... # 排他锁 DELETE FROM student WHERE ... # 排他锁 UPDATE student SET ... # 排他锁
- 读视图,用于判断快数据照可见性的逻辑,每个sql语句都会产生一个ReadView,在ReadView当中有四个核心属性
- create_trx_id 创建当前事务的id
- m_ids 当前系统活跃的事务id, 这些活跃的事务,并没有提交
- m_low_limit_id 当前活跃的事务当中最小的事务id
- m_up_limit_id 当前系统事务id的最大值的下一个事务id,也就是即将到来的事务id
可见性判断逻辑:
- 当前记录所在的事务号DB_TRX_ID,如果小于当前活跃事务最小的id,那说明是在事务开始前就已经提交了,这个版本是可见的
- 如果当前事务号大于还未即将到来的事务,说明是重新开启的事务,不用想,也知道是不可见的
- 如果当前事务号处在获取事务id的范围内,判断当前事务号是不是创建的当前事务,如果是,记录可见,如果不是,记录不可见。倘若当前事务不等于范围内的某个事务,则说明该事务已经提交了,所以当前事务也是可见的
综上基于ReadView可见性视图和undo.log日志, 得出undo.log日志保存了数据的历史快照信息,而且这些快照信息按照版本号依次串联,而ReadView的作用就是判断当前快照版本是否可读
不同隔离级别下的MVCC实现
- RC读一提交, 在一个事务当中,每一次查询都会产生新的ReadView, 这样就导致, 前后数据不一致,也就是产生了不可重复读的问题
- RR可重复读, 在一个事务开启后,只会在第一次产生ReadView,后续查询不会 在产生新的ReadView, 所以就不会出现不可重复读的情况