三分钟小短文:一致性非锁定读与一致性锁定读

台上三分钟,台下三小时,兄弟们,今天咱们花三分钟了解下数据库中的两种读(select)操作:一致性非锁定读一致性锁定读

一致性非锁定读

一致性非锁定读是什么?这里我先给出一个最最最简单的解释:一致性非锁定读就是读快照

快照即当前行数据之前的历史版本,每行记录可能存在多个历史版本,或者说每行记录可能有不止一个快照数据,一般我们将这种技术称为 行多版本技术。而由于一个行记录可能对应着多个快照(历史版本),为此不可避免地会带来一系列的并发问题,如何解决这些并发问题,就是所谓的 多版本并发控制(MVCC),当然,这不是本文的重点。

在不同事务隔离级别下,读取的方式不同。只有在事务隔离级别 READ COMMITTED 和 REPEATABLE READ(默认)下,InnoDB 存储引擎才会使用非锁定的一致性读。并且,即使都是使用非锁定的一致性读,它俩对于快照数据的定义也各不相同

  • 在 READ COMMITTED 事务隔离级别下,总是读取行的最新版本;如果行被锁定了,非一致性读不会因此去等待行上锁的释放,而是去读取该行版本的最新一个快照,如下图所示:

    img

  • 在 REPEATABLE READ 事务隔离级别下,对于快照数据,非一致性读总是读取事务开始时的行数据版本

这么说可能还不是很好理解,举个例子,这个时候又得掏出我们经典的 user 表了(滑稽),表中包含三个字段 id、username、age,已存在一行记录:

id = 1, username = 'Jack', age = 20;

1)第一步,我们开启一个事务,执行如下语句:

事务 1:
	begin;
	select * from user where id = 1;

2)可以看到,第一个事务并没有提交,这时,我们开启第二个事务模拟并发,执行如下语句:

事务 2:
	begin;
	update user set id = 100 where id = 1;

3)在第二个事务中,将表中 id 为 1 的记录修改为了 id=100,但是事务同样没有提交(即此时 id = 1 的行记录被事务 2 加上了行锁)。这时如果在第一个事务中再次读取 id 为 1 的记录,那显然还是 1 对吧:

事务 1:
	select * from user where id = 1;

4)接着,我们再来提交下第 2 个事务中所作的修改:

事务 2:
	commit;

由于当前 id = 1 的数据被修改成了 100,也就是说,当前 id = 100 的行记录拥有了一个 id = 1 的历史版本

5)这个时候,再去事务 1 中读取 id 为 1 的记录,在 READ COMMITTED 和 REPEATABLE 事务隔离级别下得到结果就不一样了:

  • 对于 READ COMMITTED 的事务隔离级别,由于事务 2 已经提交了,也就是说 id = 1 的行记录没有被事务 2 锁定,所以就会去读取该行的最新版本,即 id = 100,So, 在 READ COMMITTED 的事务隔离级别下,此时查询 id = 1 的结果是 Empty Set;

  • 而在 REPEATABLE READ 事务隔离级别下,非一致性读总是读取事务开始时的行数据版本。也就是说,在事务 1 刚开始的时候,id = 1 的数据行是什么样,现在读到的就是什么样的:

可以结合下面这张图来回顾下上述的过程:

一致性锁定读

其实从名字上也能看出来,非一致性锁定读适用于对数据一致性要求不是很高的情况,比如在 READ COMMITTED 隔离级别下,即使行被锁定了,非一致性读也可以读到该行版本的最新一个快照。也即,非锁定读机制极大地提高了数据库的并发性

而一致性锁定读适用于对数据一致性要求比较高的情况,这个时候我们需要对读操作进行加锁以保证数据逻辑的一致性。

InnoDB 存储引擎对读操作支持两种一致性锁定读方式,或者说对读操作支持两种加锁方式:

  • SELECT ... FOR UPDATE,对于读取的行记录加一个 X 排它锁,其他事务不能对锁定的行加任何锁
  • SELECT ... LOCK IN SHARE MODE,对于读取的行记录添加一个 S 共享锁。其它事务可以向被锁定的行加 S 锁,但是不允许添加 X 锁,否则会被阻塞住

So,如何用大白话解释一致性锁定读?上面这两条特殊的 select 语句就是一致性锁定读!一致性锁定读就是给行记录加 X 锁或 S 锁!

简单不?

🎉 关注公众号 | 飞天小牛肉,即时获取更新

  • 博主东南大学硕士在读,携程 Java 后台开发暑期实习生,利用课余时间运营一个公众号『 飞天小牛肉 』,2020/12/29 日开通,专注分享计算机基础(数据结构 + 算法 + 计算机网络 + 数据库 + 操作系统 + Linux)、Java 技术栈等相关原创技术好文。本公众号的目的就是让大家可以快速掌握重点知识,有的放矢。关注公众号第一时间获取文章更新,成长的路上我们一起进步

  • 并推荐个人维护的开源教程类项目: CS-Wiki(Gitee 推荐项目,现已累计 1.8k+ star), 致力打造完善的后端知识体系,在技术的路上少走弯路,欢迎各位小伙伴前来交流学习 ~ 😊

  • 如果各位小伙伴春招秋招没有拿得出手的项目的话,可以参考我写的一个项目「开源社区系统 Echo」Gitee 官方推荐项目,目前已累计 900+ star,基于 SpringBoot + MyBatis + MySQL + Redis + Kafka + Elasticsearch + Spring Security + … 并提供详细的开发文档和配套教程。公众号后台回复 Echo 可以获取配套教程,目前尚在更新中。