【Mysql】事务隔离级别与锁的关系

301 阅读3分钟

mysql锁与事务隔离级别

前两篇文章我们

学了事务【Mysql】事务相关的问题 - 掘金 (juejin.cn)

学了锁【Mysql】mysql的锁 - 掘金 (juejin.cn),我以为稳了,结果今天面试被问对应关系,真的绷不住了。

事务

事务隔离级别有 读未提交,读已提交,可重复读,串行化。

会出现的问题

  • 脏读: 事务A读取数据,事务B修改数据未提交,事务A去读取,然后事务B回滚, 事务A读到了不存在的数据,这个叫脏读。

  • 不可重复读: 事务A读取数据,事务B修改数据并提交, 事务A再读取,前后读取获得的数据不一致

  • 幻读: 事务A读取数据,事务B插入数据并提交,事务A再读取,前后数据不一致

解决办法

image.png

mysql的锁有哪些:

具体的参照文章: 【Mysql】mysql的锁 - 掘金 (juejin.cn)

  1. 全局锁, 这篇文章不涉及
  2. 表锁: 为整个表上锁
  3. 意向锁
  4. 行锁
  5. 记录锁
  6. 间隙锁
  7. 临键锁

事务与锁的对应关系

那么不同的隔离级别在读写时会加什么锁呢?

读未提交隔离级别

它会有脏读的问题,因为在读的时候是可以修改的,因此它的读写可同时进行,不会阻塞。

直接select读的时候采用当前读: 读取的永远是最新的值,读取不会获得锁。

修改数据的时候:

比如说:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
UPDATE `book` set name = '小数' where id = 1001;

这个时候会为id为1001的行添加行锁(以主键索引为查询条件): 会添加记录锁, 为表添加意向排它锁:

image.png

在「读未提交」隔离级别下,读写操作可以同时进行,但写写操作无法同时进行。与此同时,该隔离级别下只会使用行级别的记录锁,并不会用间隙锁。

读已提交隔离级别

读取数据: 同样不会获取锁,直接读取,每次读都会生成一个readview。

修改数据: 会对指定行添加记录锁,然后全表的意向排它锁。不管是使用范围查询还是等值查询,都不会用到间隙锁。

它和读未提交很像,但是它解决了脏读,因为它每次查询都是一次快照读,重建一个readview对象。

可重复读隔离级别

读取数据: 正常读取不会上锁,事务中第一次读取会获得readView对象。

修改数据的时候:

  1. 使用主键索引等值查询: 只获取了记录锁,意向排它锁: image.png image.png

  2. 使用主键索引范围查询: 获取了记录锁, 间隙锁, 意向排它锁 image.png image.png

搜索不存在的记录会加间隙锁。

  1. 使用非主键索引等值查询: 下面例子可以看到用了意向排它锁,对1001的行锁,以及下一个区间的间隙锁。image.png image.png

  2. 使用非主键索引范围查询: 因为锁的数据太多,退化为表锁,意向锁,行锁,及lock_data那里两行的临键锁,属于行锁与间隙锁的组合。

    我把非主键索引换成了version, 为了更好的范围查询。

    image.png

    image.png

Serializable隔离级别

读取: 获得共享读锁

修改: 与RR级别一致。

其实这里我觉得很奇怪, 网上写的这个级别读写都会锁住整个表,但我自己测试的时候并没有,我在修改的时候并没有上表锁,而且我另一个事务select照样可以进行,虽然会为这行添加一个共享读锁。

我也搞不明白了,反正我自己测试是不会影响的。

总结

  1. RU和RC只会用到行锁,用不到间隙锁。
  2. RR隔离级别会使用行锁,可能用到间隙锁,临键锁
  3. 串行化级别读也会为读取的行加共享读锁,写的加锁方式与RR相同