首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

如何快速理解事务隔离

1、锁

数据库中有很多跟锁相关的概念,如乐观锁、悲观锁,行锁、表锁,还有共享锁、排他锁,间隙锁等。

(具体可以参考 MySQL 的官网介绍 https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html

这里我们主要关注 3 种锁:共享锁、排他锁、间隙锁

共享锁:读锁。事务 A 对某行加读锁之后,事务 B 也可以对其加读锁。

排它锁:写锁。1、事务 A 对某行加写锁之后,其他事务无法加读锁或者写锁。2、事务 A 对某行加读锁之后,其他事务无法加写锁。

间隙锁:不管是共享间隙锁还是排它间隙锁,对某一范围的数据加上间隙锁之后都不允许在其范围内插入数据、更改数据等。

2、事务隔离

4 种隔离级别:串行化,可重复读(RR),读可提交(RC),读未提交。

下面从严格到宽松介绍这 4 种隔离级别。

2.1、串行化

涉及:写锁、读锁、间隙锁。

所有相关的数据都加上这 3 种锁,就可以实现串行化了,其他事务只能在当前事务提交并释放锁后,才能修改被锁的数据。但串行化是效率最低的,一般不考虑使用。

2.2、可重复读

涉及:写锁、读锁、偶尔用到间隙锁(Innodb)

其实用读锁与写锁就可以实现数据库规范中的可重复读,即涉及到读的行使用读锁,修改的行使用写锁。但在 Innodb 中,如果涉及到非主键并且非唯一查找的时候,会加上间隙锁,以此来杜绝此情形下的幻读问题(言外之意就是其他情形的幻读并不能解决,很多中文资料会写到 Innodb 解决了 RR 下的幻读问题,其实是不对的)。而实际上,涉及到读的数据,在 Innodb 中是依靠 MVCC 来实现的,第 3 节会提到。

2.3、读已提交

涉及:写锁、读锁

在这个级别已经完全停用了间隙锁,并且读锁也不像可重复读那样贯穿整个事务过程,只在执行语句的时候加读锁,然后就释放。这样其他事务就可以在释放间隙去更新数据并提交事务。如果当前事务再次读被更新的数据,就会发现数据变化了。这就是读已提交与可重复读的区别。而实际上,涉及到读的数据,在 Innodb 中是依靠 MVCC 来实现的,第 3 节会提到。

2.4、读未提交

涉及:写锁

在这个级别里面,读锁已经不需要存在了,因为其他事务未提交的数据是能被当前事务读取到的。但这会产生脏读的问题。一般不考虑使用。

2.5、小结

可以明显看到,随着锁类型和使用时机的减少,隔离级别从严格一步步演化到宽松。在实际使用中,一般只考虑可重复读或者读已提交。

(本节参考链接:https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html

3、MVCC

全称:multi-version concurrency control ,有时候也叫做 multi-versioning

简单得理解,就是一个快照(文章不涉及 MVCC 的实现细节)。根据快照的时机分为当前读和快照读。而快照的时机,我分为以下两种:

3.1、时机 1

根据隔离级别进行快照。

如果是可重复读,会在事务开始第一次读相关数据的时候进行快照,第二次遇到该数据,则还是从快照读取。以此实现隔离级别 可重复读 的数据一致性问题,也解答了 2.2 中遗留的问题。

如果是读已提交,会在每次读取数据的时候都进行快照,并且根据规则获取其他事务已提交的数据。以此实现隔离级别 读已提交 的数据读取问题,也解答了 2.3 中遗留的问题。

3.2、时机 2

根据执行语句进行快照。

当执行 locking read 语句时会重新进行快照,即使是在可重复读之下。

如:SELECT*FROM t FORSHARE;

这个方式属于主动型进行当前读快照。

(本节参考链接:https://dev.mysql.com/doc/refman/8.0/en/innodb-consistent-read.html

4、总结

利用锁可以更好地理解事务隔离的定义与实现,再配合 MVCC 的快照时机,理解 Innodb 的具体实现。

这个过程下来,可以较好的记住事务隔离相关的内容。

通过每节的参考链接可以更好地理解相关的内容。

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/6c42f8ff373d272a520796f06
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券