台上三分钟,台下三小时,兄弟们,今天咱们花三分钟了解下数据库中的两种读(select)操作:一致性非锁定读 和 一致性锁定读
一致性非锁定读是什么?这里我先给出一个最最最简单的解释:一致性非锁定读就是读快照!
快照即当前行数据之前的历史版本,每行记录可能存在多个历史版本,或者说每行记录可能有不止一个快照数据,一般我们将这种技术称为 行多版本技术。而由于一个行记录可能对应着多个快照(历史版本),为此不可避免地会带来一系列的并发问题,如何解决这些并发问题,就是所谓的 多版本并发控制(MVCC),当然,这不是本文的重点。
在不同事务隔离级别下,读取的方式不同。只有在事务隔离级别 READ COMMITTED 和 REPEATABLE READ(默认)下,InnoDB 存储引擎才会使用非锁定的一致性读。并且,即使都是使用非锁定的一致性读,它俩对于快照数据的定义也各不相同:
img
这么说可能还不是很好理解,举个例子,这个时候又得掏出我们经典的 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 隔离级别下,即使行被锁定了,非一致性读也可以读到该行版本的最新一个快照。也即,非锁定读机制极大地提高了数据库的并发性。
而一致性锁定读适用于对数据一致性要求比较高的情况,这个时候我们需要对读操作进行加锁以保证数据逻辑的一致性。
InnoDB 存储引擎对读操作支持两种一致性锁定读方式,或者说对读操作支持两种加锁方式:
SELECT ... FOR UPDATE
,对于读取的行记录加一个 X 排它锁,其他事务不能对锁定的行加任何锁SELECT ... LOCK IN SHARE MODE
,对于读取的行记录添加一个 S 共享锁。其它事务可以向被锁定的行加 S 锁,但是不允许添加 X 锁,否则会被阻塞住So,如何用大白话解释一致性锁定读?上面这两条特殊的 select 语句就是一致性锁定读!一致性锁定读就是给行记录加 X 锁或 S 锁!
简单不?
我是小牛肉,长风破浪会有时,小伙伴们下篇文章再见 👋
CS-Wiki
「Gitee 官方推荐项目,现已 1.9k+ star,仓库地址:https://gitee.com/veal98/CS-Wiki」,公众号上的文章也会在此同步更新,欢迎各位前来交流学习Echo
「Gitee 官方推荐项目,现已 1.1k+ star,仓库地址:https://gitee.com/veal98/Echo」。