引言
通过构造示例,理解Mysql的隔离级别及其锁和MVCC的实现
参考极客时间mysql45讲之第3讲,7讲和8讲,19讲,20讲
事务隔离级别
- RU 读未提交
- RC 读已提交
- RR 可重复读
- S 顺序读
1 | 表结构 |
在四种模式下分别试验例子1
1 | 例子1: |
- s顺序读 读加读锁 写加写锁 写锁必须等待读锁完成. 事务B的第三部阻塞,v1,v2都是1,v3是2,简单但是并发性能不好
- RR V1,v2都是1,v3是2 读不加锁 mvcc
会有大量的undo log,并且如果有大量的事务会大致读取时遍历undolog导致读取性能下降 - RC V1是1,v2是2,v3是2 会导致不可重复读
- RU v1,v2,v3都是2 会导致脏读
重点看RR:
- 如果事务A的第2步挪到事务B的第三步之后,效果如何.即读取视图是从何时开始的,应该是从第一个查询语句开始 (此时如果在A的第2步之前再加一个update语句会如何)
查询时才会开启一个事务
1 | 事务A 事务B |
- 如果在A中update之后在B中再次update,会如何
修改一下:
1 | 事务A 事务B |
写代码的时候需要判断影响行数,即是否真正执行了更新
再修改一下:最后得到的值是2
1 | 事务A 事务B |
- 上边示例中同时更新一行会有行锁,我们演示一下行锁
行锁在事务开始时加,但并不是语句结束后就释放, 而是事务结束后才释放,称之为两阶段锁协议.所以在一个事务中将最可能造成锁争用的语句放到事务最后执行
行锁通过锁索引记录来实现,如果没有索引,锁全表.
1 | delete from T; |
事务A更新1,事务B更新5
1 | 事务A 事务B |
修改c增加uniq index
create unique index u_index_c on T (c
);
1 | 事务A 事务B |
事务A在1字段的唯一索引上加锁,所以事务B的第2条语句不会被阻塞
drop index u_index_c on T;
create index u_index_c on T (c
);
修改c增加 index
1 | 事务A 事务B |
此时的加锁原则是等值加锁,向右查找到第一个不满足条件的记录后退化为gap锁
但如果第2条语句换成插入3 例如insert into T values (3);会被阻塞
1 | 事务A 事务B |
如果有3条记录,1,2,5 那么插入记录3是可以插入的
1 | 事务A 事务B |