- MVCC是什么呢?
MVCC其实就是一个多版本并发控制,即多个不同版本的数据实现并发控制的技术,其基本思想是为每次事务生成一个新版本的数据,
在读数据时选择不同版本的数据即可以实现对事务结果的完整性读取。
- MVCC主要有什么作用呢?
提高并发读写性能,操作时会生成事务id
1》每条记录都会保存两个隐藏列:【trx_id】(事务id)和roll_pointer(回滚指针
2》每次操作都会生成一条undo_log日志,回滚指针指向前一条记录
查询的时候会读取出【read-view】:[未提交的事务id]数组+最大事务id,并根据read-view从undo_log日志中最新的记录一次往下找,查找规则如下:
①从最新记录开始查找:
如果当前记录:事务id < 未提交事务的最小id,则可读
如果当前记录:最小id <= 事务id <= 事务的最小id,则判断事务id是否在未提交事务id的数组中,若在则不可读(如果只有自己还是可读)
如果当前记录:事务id > 事务的最大id,则不可读
②可重复读返回的是第一次查询生成的【read-view】,读已提交每次都会重新生成一个新的【read-view】
MVCC只能实现读已提交和可重复读;如果是读未提交,那么每次查询都能获取最新的修改值。
- Don‘t BB,看个案例先回顾一下事物的隔离级别:
那么事务A在提交前后,事务B读取到的x值是什么样的呢?答案是:事务B在不同隔离级别下,读取到的值不一样。
1》如果隔离级别是读未提交(Read-Uncommited),两次读到的数据都是20,(个人理解读未提交,就是事务A未提交的修改都可以读到)
2》如果隔离级别是读已提交(Read-Committed),第一次读取到的数据是10,第二次读取到的数据是20(我个人理解读已提交,就是只有事务A已提交的修改才可以读到)
3》如果隔离级别是可重复度(Repeatable-Read),两次读到的数据都是20(我个人有理解,读已提交就是事务B有一个很强的隔离性,只要在同一事务内,读到的数据都是相同的)
-
当执行查询SQL时,会生成一致性视图叫【read-view】,它由执行查询时所未提交事务id数组(数组里最小的事务id为【min_id】和已创建的最大事务id为【max_id】组成),
查询的数据结果要跟【read-view】做比较,从而得到快照结果。话不多少,直接上个案例:
事务1 | 事务2 | 事务3 | 事务4 |
update table set name = ‘A‘ where id = 1 | |||
update table set name = ‘B‘ where id = 1 | |||
commit | update table set name = ‘C‘ where id = 1 | ||
update table set name = ‘C‘ where id = 1 | select name from table where id = 1 ; read-view:[1,3] 3 | ||
commit | |||
commit | select name from table where id = 1 |
Undo日志如下:
事务4在第一次查询过程是这样:
1》先比对【undo_log①】,事务id为1,read-view为[1,3] 3,不符合条件,继续向下比对; 2》比对【undo_log②】,事务id为3,事务id > 事务的最大id,所以也不可读; 3》比对【undo_log③】,事务id为2,最小id <= 事务id <= 事务的最小id,并且不在未提交事务id数组中,所以可以,所以查询出来的【name = B】; 我们可以根据表格看出,事务2已提交,所以结果就应该B,正确。
- 读已提交是如何使用【MVCC】实现的呢?
每次查询的时候生成一个新的【read-view】,然后去【undo】日志中寻找符合结果的一条数据。
所以事务4对应的【read-view】应该为:[1] 3,第二次查询【name = ‘C‘】
- 可重复读是如何使用【MVCC】实现的呢?
因为每次查询的时候都会生成一个新的【read-view】,但是如果是可重复读的时候,
第一次查询的时候生成一个【read-view】,后面查询的时候便会复用第一次的【read-view】,然后找出一个可读的数据
所以事务4对应的【read-view】应该为:[1,3] 3,第二次查询【name = ‘B‘】
- 不过可串行化又是如何实现的呢?
串行化会在读取的每一行数据上都加锁(这个意思是部分锁,但不会锁表)
串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞