目录
1.什么是mvcc?
2.mvcc中的快照读和当前读有什么区别和联系?
3.mvcc的作用是什么?
4.mvcc的实现机制和原理是什么?
1.什么是mvcc?
mvcc全称是(Multi-Version Concurrency Control) 多版本并发控制,是数据库管理过程中的一种并发控制方式。数据库库中的并发控制一般是加锁。线程在操作共享资源时,需要加锁解决并发访问控制。锁又分为乐观锁和悲观锁。
悲观锁:一上来,认为数据库会发生并发冲突,先锁住数据,不允许其他事物操作,直到本事务提交为止。
乐观锁:在不锁定的情况下,更新数据,如果发现版本不一致,不更新数据,通过增加版本号比对来实现,还有cas等方式。
mvcc本质是一种乐观锁的实现方式。
2.mvcc中的快照读和当前读有什么区别和联系?
mvcc有两种读的形式:快照读和当前读。
快照读:读写不冲突,每次读取到快照数据。例如 普通的select语句。
当前读:每次读取到当前的最新数据。例如 select ...for update/select ... lock in share mode/delete /delete/insert
在不同的隔离级别下,快照读和当前读的读取到的数据可能不同
在repeated-read隔离级别下,快照度可能读取的不是最新的数据,当前读读取的是最新的数据。
在read committed隔离级别下,快照读和当前读读取的是最新的数据,快照度等价于当前读
3.mvcc的作用是什么?
mysql默认的隔离级别是可重复读,快照读(select语句),使用mvcc解决了幻读的问题。当前读(select ... for update) ,通过(next-key-lock)记录锁和间隙锁来解决幻读。
mvcc在很大程度上避免了幻读,但是没有彻底的解决。有下面的两个场景没有解决幻读
案例1 对于快照读 事务A更新了事务B已经插入的记录,A事务两次查询结果记录条数不一致
user表数据
id | name | age |
1 | 小张 | 11 |
2 | 小李 | 22 |
3 | 小王 | 33 |
4 | 小赵 | 44 |
id | 事务A | 事务B |
1 | begin | begin |
2 | select * from user where id=5 | |
3 | insert into users values(5,"小花",23) commit; | |
4 | update users set name="小明" where id=5 | |
5 | select * from user where id=5 |
事务A的第1行和第5行的查询结果条数不一样,发生了幻读。
案例2 对于当前读 事务A先执行快照读,事务B插入了一条记录,事务A再执行当前读,导致两次查询结果记录数不同
还是上面的user数据集
id | 事务A | 事务B |
1 | begin | begin |
2 | select * from user where id>=1 | |
3 | insert into users values(5,"小花",23) commit; | |
4 | select * from user where id>=1 for update |
事务A的第1行和第4行的查询结果条数不一样,发生了幻读。
解决方案:事务开启后,先执行一次当前读 select for update,它会对当前事务加next-key-lock,避免其他事务插入记录。
4.mvcc的实现机制和原理是什么?
mvcc是通过数据库的三个字段DB_TRX_ID(事务id),DB_ROLL_PTR(回滚指针),isDelete(是否删除的字段),undoLog(回滚日志)和read-view(读视图)来实现mvcc的可见性算法。
read-view是每个事务在进行一个select语句(快照读)时,产生一个read-view(读视图)有三个字段alive_trx_list,up_limit_id,low_limit_id,具体含义如下
alive_trx_list:正在活跃的事务id列表
up_limit_id:当前最小的事务id
low_limit_id:当前最大事务id+1
TRX_Id:当前记录的事务id
可见性算法分三种情况
1. TRX_Id<up_limit_id|| TRX_Id==CUR_TRX_ID 可见,返回对应的记录
2. TRX_Id>=low_limit_id 不可见 通过回滚指针找到上一条记录,继续判断,直到指针为空
3. TRX_Id in alive_trx_list 不可见,事务未提交,通过回滚指针找到上一条记录,继续判断,指针指针为空,否则可见,返回对应的记录
可见性算法执行流程图如下