MySQL 事务并发执行时的一致性问题

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: MySQL 事务并发执行时的一致性问题

MySQL 是一个客户端/服务器架构的软件。对于同一个服务器来说,可以有多个客户端与之连接。每个客户端与服务器建立连接后,就形成了一个会话。每个客户端都可以在自己的会话中向服务器发出请求语句,一个请求语句可能是某个事务的一部分。服务器可以同时处理来自多个客户端的多个事务。

在某个事务访问某个数据时,如要求其他试图访问相同数据的事务进行限制 , 让它们进行排队。当该事务提交之后,其他事务才能继续访问这个数据。这样可以让并发执行的事务的执行结果与串行执行的结果一样,我们把这种多个事务的执行方式称为可串行化执行。

## 事务并发执行时遇到的一致性问题

### 脏写(Dirty Write)

如果一个事务修改了另一个未提交事务修改过的数据,就意味着发生了脏写现象。我们可以把脏写现象简称为 PO 。假设现在事务 T1 和 T2 并发执行 , 它们都要访问数据项 x (这里可以将数据项 x 当作一条记录的某个字段).那么 PO 对应的操作执行序列如下所示 :

```

P0:w1[x]...w2[x]...((c1 or a1) and (c2 or a2) in any order)

```

其中 w1[x]表示事务 T1 修改了数据项 x 的值。w2[x] 表示事务 T2 修改了数据项 x 的值,C1 表示事务 T1 的提交 (Commit),a1 表示事务 T1 的中止 (Abort),c2 表示事务 T2 的提交,a2 表示事务 T2 的中止,...表示其他的一些操作。从 P0 的操作执行序列中可以看出,事务 T2 修改了未提交事务 T1 修改过的数据, 所以发生了脏写现象。

脏写现象也可能破坏原子性和持久性。比方说有 x 和 y 这两个数据项,它们初始的值都是 0。 两个并发执行的事务 T1 和 T2 有下面的操作执行序列:

```

w1[x=2]w2[x=3]w2[y=3]c2a1

```

现在的问题是 T1 中止时, 需要将它对数据库所做的修改回滚到该事务开启时的样子,也就是将数据项 x 的值修改为 0 . 但是此时 T2 已经修改过数据项 x 并且提交了,如果要将 T1 回滚的话,相当于要对 T2 对数据库所做的修改进行部分回滚(部分回滚是指只回滚对 x 做的修改,而不回滚对 y 做的修改),这就影响到了事务的原子性。如果要将 T2 对数据库所做的修改全部回滚的话 , 那么明明 T2 己经提交了,它对数据库所做的修改应该具有持久性,怎么能让一个未提交的事务将 T2 的持久性破坏掉呢?

### 脏读(Dirty Read)

如果一个事务读到了另一个未提交事务修改过的数据,就意味着发生了脏读现象,我们可以把脏读现象简称为 P1。 假设现在事务 T1 和 T2 并发执行,它们都要访问数据项 x。 那么 P1 对应的操作执行序列如下所示 :

```sql

P1:w1[x]...r2[x]...(( c1 or a1) and (c2 or a2) in any order)

```

脏读现象也可能引发一致性问题。比方说事务 T1 和 T2 中要访问 x 和 y 这两个数据项,我们的一致性需求就是让 x 的值和 y 的值始终相同,x 和 y 的初始值都是 0. 现在并发执行事务 T1 和 T2,它们的操作执行序列如下所示 :

```sql

w1[x=1]r2[x=1]r2[y=0]c2w1[y=1]c1

```

很显然 T2 是一个只读事务,依次读取 x 和 y 的值。可是由于 T2 读取的数据项 x 是未提交事务 T1 修改过的值,所以导致最后读取 x 的值为 1,Y 的值为 0 。虽然最终数据库状态还是一致的(最终变为了 x=1,y=1),但是 T2 却得到了一个不一致的状态。数据库的不一致状态是不应该暴露给用户的。P1 代表的事务的操作执行序列其实是一种脏读的广义解释,针对脏读还有一种严格解释。为了与广义解释进行区分,我们把脏读的严格解释称为 A1。 A1 对应的操作执行序列如下所示 :

```sql

A1:w1[x]...r2[x]...(a1 and c2 in any order)

```

也就是 T1 先修改了数据项 x 的值,然后 T2 又读取了未提交事务 T1 针对数据项 x 修改后的值,之后 T1 中止而 T2 提交。这就意味着 T2 读到了一个根本不存在的值,这也是脏读的严格解释。很显然脏读的广义解释是覆盖严格解释包含的范围的。

### 不可重复读(Non-Repeatable Read)

如果一个事务修改了另一个未提交事务读取的数据 ,就意味着发生了不可重复读现象,或者叫模糊读 ( Fuuzzy Read) 现象。我们可以把不可重复读现象简称为 P2。假设现在事务 T1 和  T2 并发执行,它们都要访问数据项 x。那么 P2 对应的操作执行序列如下所示:

```sql

P2: r1[x]...w2[x]...((c1 or a1) and (c2 or a2) in any order)  

```

不可重复读现象也可能引发一致性问题。比方说事务 T1 和 T2 中要访问 x 和 y 这两个数据项,我们的一致性需求就是让 x 的值和 y 的值始终相同,x 和 y 的初始值都是 0。现在并发执行事务 T1 和 T2。它们的操作执行序列如下所示:

```sql

r1[x=O]w2[x=1]w2[y=1]c2r1[y=1]c1  

```

很显然 Tl 是一个只读事务,依次读取 x 和 y 的值.可是由于 T1 在读取数据项 x 后,T2 接着修改了数据项 x 和 y 的值,并且提交,之后 T1 再读取数据项 y。这个过程中虽未发生脏写和脏读(因为 T1 读取 y 的值时,T2 已经提交),但最终 T1 得到的 x 的值为 0, Y 的值为 1。很显然这是一个不一致的状态,这种不一致的状态是不应该暴露给用户的。

P2 代表的事务的操作执行序列其实是一种不可重复读的广义解释,针对不可重复读还有一种严格解释。为了与广义解释进行区分,我们把不可重复读的严格解释称为 A2。A2 对应的操作执行序列如下所示:

```sql

A2:r1[x]...w2[x]...c2...r1[x]...c1

```

也就是 T1 先读取了数据项 x 的值,然后 T2 又修改了未提交事务 T1 读取的数据项 x 的值,之后 T2 提交,然后 T1 再次读取数据项 x 的值时会得到与第一次读取时不同的值。这也是不可重复读的严格解释。很显然不可重复读的广义解释是覆盖严格解释包含的范围的。

### 幻读(Phantom)

如果一个事务先根据某些搜索条件查询出一些记录,在该事务未提交时,另一个事务写入一些了符合那些搜索条件的记录(这里的写入可以指 INSERT、 DELETE、UPDATE 操作),就意味着发生了幻读现象。我们可以把幻读现象简称为 P3。假设现在事务 T1 和 T2 并发执行, 那么 P3 对应的操作执行序列如下所示 :

```sql

P3:r1[P]...w2[y in P]...((c1 or a1) and (c2 or a2) any order)

```

其中 r1[P] 表示 T1 读取一些符合搜索条件 P 的记录,w2[y in P]表示 T2 写入一些符合搜索条件 P 的记录。

幻读现象也可能引发一致性问题。比方说现在符合搜索条件 P 的记录条数有 3 条。我们有一个数据项 z 专门表示符合搜索条件 P 的记录条数,它的初始值当然也是 3。我们的一致性需求就是

让 z 表示符合搜索条件 P 的记录数.现在并发执行事务 T1 和 T2,它们的操作执行序列如下所示:

```sql

r1[P]w2[insert y to P]r2[z=3]w2[z=4]c2r1[z=4]c1

```

T1 先读取符合搜索条件 P 的记录,然后 T2 插入了一条符合搜索条件 P 的记录,并且更

新数据项 z 的值为 4。然后 T2 提交,之后 T1 再读取数据项 z。z 的值变为了 4,这与 T1 之前

实际读取出的符合搜索条件 P 的记录条数不合,不符合一致性需求。

P3 代表的事务的操作执行序列其实是一种幻读的广义解释,针对幻读还有一种严格解释。

为了与广义解释进行区分,我们把幻读的严格解释称为 A3, A3 对应的操作执行序列如下所示:

```sql

A3:r1[p]...w2[y in P]...c2...r1[p]...c1

```

也就是 T1 先读取符合搜索条件 P 的记录,然后 T2 写入了符合搜索条件 P 的记录。之后  T1 再读取符合搜索条件 P 的记录时,会发现两次读取的记录是不一样的。

#### MySQL 中的幻读

上面关于脏写、脏读、不可重复读、幻读的讨论均属于理论范畴,不涉及具体数据库。对于 MySQL 来说,幻读强调的就是一个事务在按照某个相同的搜索条件多次读取记录时,在后读取时读到了之前没有读到的记录。这个「后读取到的之前没有读到的记录」可以是由别的事务执行 INSERT 语句插入的,也可能是别的事务执行了更新记录键值的 UPDATE 语句而插入的。这些之前读取时不存在的记录也可以被称为幻影记录。假设 T1 先根据搜索条件 P 读取了一些记录,接着 T2 删除了一些符合搜索条件 P 的记录后提交,如果 T1 再读取符合相同搜索条件的记录时获得了不同的结果集,我们就可以把这种现象认为是结果集中的每一条记录分别发生了不可重复读现象。

## 参考资料

- 《A critique of ANSI SQL isolation levels》

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
8天前
|
SQL 存储 缓存
【MySQL】事务
【MySQL】事务
16 0
|
8天前
|
SQL 存储 关系型数据库
MySQL索引及事务
MySQL索引及事务
29 2
|
8天前
|
存储 关系型数据库 MySQL
MySQL事务简述
MySQL事务简述
8 0
|
8天前
|
存储 算法 关系型数据库
MySQL事务与锁,看这一篇就够了!
MySQL事务与锁,看这一篇就够了!
|
8天前
|
Java 关系型数据库 MySQL
MySQL 索引事务
MySQL 索引事务
14 0
|
8天前
|
SQL 安全 关系型数据库
【Mysql-12】一文解读【事务】-【基本操作/四大特性/并发事务问题/事务隔离级别】
【Mysql-12】一文解读【事务】-【基本操作/四大特性/并发事务问题/事务隔离级别】
|
8天前
|
存储 关系型数据库 MySQL
Mysql学习--深入探究索引和事务的重点要点与考点
Mysql学习--深入探究索引和事务的重点要点与考点
|
6天前
|
关系型数据库 MySQL API
实时计算 Flink版产品使用合集之可以通过mysql-cdc动态监听MySQL数据库的数据变动吗
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
82 0
|
8天前
|
关系型数据库 MySQL 数据库
docker MySQL删除数据库时的错误(errno: 39)
docker MySQL删除数据库时的错误(errno: 39)
64 0
|
8天前
|
Java 关系型数据库 MySQL
【MySQL × SpringBoot 突发奇想】全面实现流程 · xlsx文件,Excel表格导入数据库的接口(下)
【MySQL × SpringBoot 突发奇想】全面实现流程 · xlsx文件,Excel表格导入数据库的接口
46 0