55 - MySQL
66---
77
8-
98> 本文由 [ SnailClimb] ( https://github.com/Snailclimb ) 和 [ guang19] ( https://github.com/guang19 ) 共同完成。
109
1110## 事务隔离级别(图文详解)
1413
1514事务是逻辑上的一组操作,要么都执行,要么都不执行。
1615
17- 事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账1000元 ,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元 。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
16+ 事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账 1000 元 ,这个转账会涉及到两个关键操作就是:将小明的余额减少 1000 元,将小红的余额增加 1000 元 。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
1817
1918### 事务的特性(ACID)
2019
2120![ 事务的特性] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/事务特性.png )
2221
23-
24221 . ** 原子性:** 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
25232 . ** 一致性:** 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
26243 . ** 隔离性:** 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
3129在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对统一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。
3230
3331- ** 脏读(Dirty read):** 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
34- - ** 丢失修改(Lost to modify):** 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务1读取某表中的数据A =20,事务2也读取A =20,事务1修改A =A-1,事务2也修改A =A-1,最终结果A =19,事务1的修改被丢失 。
32+ - ** 丢失修改(Lost to modify):** 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务 1 读取某表中的数据 A =20,事务 2 也读取 A =20,事务 1 修改 A =A-1,事务 2 也修改 A =A-1,最终结果 A =19,事务 1 的修改被丢失 。
3533- ** 不可重复读(Unrepeatableread):** 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
3634- ** 幻读(Phantom read):** 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
3735
3836** 不可重复度和幻读区别:**
3937
4038不可重复读的重点是修改,幻读的重点在于新增或者删除。
4139
42- 例1 (同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
40+ 例 1 (同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务 1 中的 A 先生读取自己的工资为 1000 的操作还没完成,事务 2 中的 B 先生就修改了 A 的工资为 2000,导 致 A 再读自己的工资时工资变为 2000;这就是不可重复读。
4341
44- 例2 (同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条 ,这样就导致了幻读。
42+ 例 2 (同样的条件, 第 1 次和第 2 次读出来的记录数不一样 ):假某工资单表中工资大于 3000 的有 4 人,事务 1 读取了所有工资大于 3000 的人,共查到 4 条记录,这时事务 2 又插入了一条工资大于 3000 的记录,事务 1 再次读取时查到的记录就变为了 5 条 ,这样就导致了幻读。
4543
4644### 事务隔离级别
4745
4846** SQL 标准定义了四个隔离级别:**
4947
5048- ** READ-UNCOMMITTED(读取未提交):** 最低的隔离级别,允许读取尚未提交的数据变更,** 可能会导致脏读、幻读或不可重复读** 。
5149- ** READ-COMMITTED(读取已提交):** 允许读取并发事务已经提交的数据,** 可以阻止脏读,但是幻读或不可重复读仍有可能发生** 。
52- - ** REPEATABLE-READ(可重复读):** 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,** 可以阻止脏读和不可重复读,但幻读仍有可能发生** 。
53- - ** SERIALIZABLE(可串行化):** 最高的隔离级别,完全服从ACID的隔离级别 。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,** 该级别可以防止脏读、不可重复读以及幻读** 。
50+ - ** REPEATABLE-READ(可重复读):** 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,** 可以阻止脏读和不可重复读,但幻读仍有可能发生** 。
51+ - ** SERIALIZABLE(可串行化):** 最高的隔离级别,完全服从 ACID 的隔离级别 。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,** 该级别可以防止脏读、不可重复读以及幻读** 。
5452
55- ----
53+ ---
5654
57- | 隔离级别 | 脏读 | 不可重复读 | 幻读 |
58- | :---: | :--- : | :---: | :- --: |
59- | READ-UNCOMMITTED | √ | √ | √ |
60- | READ-COMMITTED | × | √ | √ |
61- | REPEATABLE-READ | × | × | √ |
62- | SERIALIZABLE | × | × | × |
55+ | 隔离级别 | 脏读 | 不可重复读 | 幻读 |
56+ | :-------------- : | :--: | :--------: | :--: |
57+ | READ-UNCOMMITTED | √ | √ | √ |
58+ | READ-COMMITTED | × | √ | √ |
59+ | REPEATABLE-READ | × | × | √ |
60+ | SERIALIZABLE | × | × | × |
6361
6462MySQL InnoDB 存储引擎的默认支持的隔离级别是 ** REPEATABLE-READ(可重读)** 。我们可以通过` SELECT @@tx_isolation; ` 命令来查看,MySQL 8.0 该命令改为` SELECT @@transaction_isolation; `
6563
@@ -86,9 +84,9 @@ InnoDB 存储引擎在 **分布式事务** 的情况下一般会用到 **SERIALI
8684
8785### 实际情况演示
8886
89- 在下面我会使用 2 个命令行mysql ,模拟多线程(多事务)对同一份数据的脏读问题。
87+ 在下面我会使用 2 个命令行 mysql ,模拟多线程(多事务)对同一份数据的脏读问题。
9088
91- MySQL 命令行的默认配置中事务都是自动提交的,即执行SQL语句后就会马上执行 COMMIT 操作。如果要显式地开启一个事务需要使用命令:` START TRANSACTION ` 。
89+ MySQL 命令行的默认配置中事务都是自动提交的,即执行 SQL 语句后就会马上执行 COMMIT 操作。如果要显式地开启一个事务需要使用命令:` START TRANSACTION ` 。
9290
9391我们可以通过下面的命令来设置隔离级别。
9492
@@ -104,39 +102,29 @@ SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTE
104102
105103#### 脏读(读未提交)
106104
107- <div align =" center " >
108- <img src =" https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-31-1脏读(读未提交)实例.jpg " width =" 800px " />
109- </div >
105+ ![ ] ( < https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-31-1脏读(读未提交)实例.jpg > )
110106
111107#### 避免脏读(读已提交)
112108
113- <div align =" center " >
114- <img src =" https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-31-2读已提交实例.jpg " width =" 800px " />
115- </div >
109+ ![ ] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-31-2读已提交实例.jpg )
116110
117111#### 不可重复读
118112
119113还是刚才上面的读已提交的图,虽然避免了读未提交,但是却出现了,一个事务还没有结束,就发生了 不可重复读问题。
120114
121- <div align =" center " >
122- <img src =" https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-32-1不可重复读实例.jpg " />
123- </div >
115+ ![ ] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-32-1不可重复读实例.jpg )
124116
125117#### 可重复读
126118
127- <div align =" center " >
128- <img src =" https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-33-2可重复读.jpg " />
129- </div >
119+ ![ ] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-33-2可重复读.jpg )
130120
131121#### 幻读
132122
133123##### 演示幻读出现的情况
134124
135- <div align =" center " >
136- <img src =" http://101.43.132.98:98/images/phantom_read.png " />
137- </div >
125+ ![ ] ( https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/database/phantom_read.png )
138126
139- sql 脚本1 在第一次查询工资为 500 的记录时只有一条,sql 脚本 2 插入了一条工资为 500 的记录,提交之后;sql 脚本 1 在同一个事务中再次使用当前读查询发现出现了两条工资为 500 的记录这种就是幻读。
127+ sql 脚本 1 在第一次查询工资为 500 的记录时只有一条,sql 脚本 2 插入了一条工资为 500 的记录,提交之后;sql 脚本 1 在同一个事务中再次使用当前读查询发现出现了两条工资为 500 的记录这种就是幻读。
140128
141129幻读和不可重复读有些相似之处 ,但是不可重复读的重点是修改,幻读的重点在于新增或者删除。
142130
@@ -146,13 +134,13 @@ sql 脚本1 在第一次查询工资为 500 的记录时只有一条,sql 脚
146134
1471351 . 将事务隔离级别调整为 ` SERIALIZABLE `
1481362 . 在可重复读的事务级别下,给事务操作的这张表添加表锁
149- 3 . 在可重复读的事务级别下,给事务操作的这张表添加 ` Next-Key Locks `
137+ 3 . 在可重复读的事务级别下,给事务操作的这张表添加 ` Next-Key Locks `
150138
151139> 说明:` Next-Key Locks ` 相当于 行锁 + 间隙锁
152140
153141### 参考
154142
155- - 《MySQL技术内幕:InnoDB存储引擎 》
143+ - 《MySQL 技术内幕:InnoDB 存储引擎 》
156144- < https://dev.mysql.com/doc/refman/5.7/en/ >
157145- [ Mysql 锁:灵魂七拷问] ( https://tech.youzan.com/seven-questions-about-the-lock-of-mysql/ )
158146- [ Innodb 中的事务隔离级别和锁的关系] ( https://tech.meituan.com/2014/08/20/innodb-lock.html )
0 commit comments