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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
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》

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
存储 关系型数据库 MySQL
MySQL MVCC全面解读:掌握并发控制的核心机制
【10月更文挑战第15天】 在数据库管理系统中,MySQL的InnoDB存储引擎采用了一种称为MVCC(Multi-Version Concurrency Control,多版本并发控制)的技术来处理事务的并发访问。MVCC不仅提高了数据库的并发性能,还保证了事务的隔离性。本文将深入探讨MySQL中的MVCC机制,为你在面试中遇到的相关问题提供全面的解答。
218 2
|
1天前
|
SQL 安全 关系型数据库
【MySQL基础篇】事务(事务操作、事务四大特性、并发事务问题、事务隔离级别)
事务是MySQL中一组不可分割的操作集合,确保所有操作要么全部成功,要么全部失败。本文利用SQL演示并总结了事务操作、事务四大特性、并发事务问题、事务隔离级别。
【MySQL基础篇】事务(事务操作、事务四大特性、并发事务问题、事务隔离级别)
|
7天前
|
SQL 关系型数据库 MySQL
MySQL进阶突击系列(04)事务隔离级别、AICD、CAP、BASE原则一直搞不懂? | 看这篇就够了
本文详细介绍了数据库事务的四大特性(AICD原则),包括原子性、隔离性、一致性和持久性,并深入探讨了事务并发问题与隔离级别。同时,文章还讲解了分布式系统中的CAP理论及其不可能三角关系,以及BASE原则在分布式系统设计中的应用。通过具体案例和图解,帮助读者理解事务处理的核心概念和最佳实践,为应对相关技术面试提供了全面的知识准备。
|
2月前
|
存储 SQL 关系型数据库
MySQL的事务隔离级别
【10月更文挑战第17天】MySQL的事务隔离级别
127 43
|
1月前
|
关系型数据库 MySQL
mysql事务特性
原子性:一个事务内的操作统一成功或失败 一致性:事务前后的数据总量不变 隔离性:事务与事务之间相互不影响 持久性:事务一旦提交发生的改变不可逆
|
1月前
|
缓存 NoSQL 关系型数据库
mysql和缓存一致性问题
本文介绍了五种常见的MySQL与Redis数据同步方法:1. 双写一致性,2. 延迟双删策略,3. 订阅发布模式(使用消息队列),4. 基于事件的缓存更新,5. 缓存预热。每种方法的实现步骤、优缺点均有详细说明。
|
28天前
|
关系型数据库 MySQL 数据库
MySQL事务隔离级别及默认隔离级别的设置
在数据库系统中,事务隔离级别是一个关键的概念,它决定了事务在并发执行时如何相互隔离。MySQL提供了四种事务隔离级别,每种级别都解决了不同的并发问题。本文将详细介绍这些隔离级别以及MySQL的默认隔离级别。
|
1月前
|
存储 关系型数据库 MySQL
MySQL MVCC深度解析:掌握并发控制的艺术
【10月更文挑战第23天】 在数据库领域,MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种重要的并发控制机制,它允许多个事务并发执行而不产生冲突。MySQL作为广泛使用的数据库系统,其InnoDB存储引擎就采用了MVCC来处理事务。本文将深入探讨MySQL中的MVCC机制,帮助你在面试中自信应对相关问题。
163 3
|
3天前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
13 3
|
3天前
|
安全 关系型数据库 MySQL
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
20 3