解读Mysql8中的事务隔离级别

事务的隔离级别与几个关键词有关: 脏数据,不可重复读,幻读,一致性读

假设有一个 accounts 表:

diff 复制代码
+----+---------+
| id | balance |
+----+---------+
|  1 | 1000.00 |
|  2 |  0.00 |
+----+---------+

READ UNCOMMITED (读未提交模式)

Shell A Shell B
set session transaction isolation level read uncommitted; set session transaction isolation level read uncommitted;
start transaction; start transaction;
select * from accounts where id=1; select * from accounts where id=1;
结果是: ` 1
update accounts set balance=balance+100 where id=1;
结果是: Query OK, 1 row affected (0.01 sec)
select * from accounts where id=1; select * from accounts where id=1;
结果是: ` 1
rollback
select * from accounts where id=1; select * from accounts where id=1;
结果是: ` 1

这个案例中, Shell B 执行 rollback 之前, Shell A 就读取到了 Shell B 存入的数据, 此时账户中的 100 就叫 脏数据, 如果 Shell B 在真实业务中执行了 rollback, 那此时 Shell A 中读取到的就是错误数据, 如果是在银行业务中出现了此类问题, 将会产生严重的后果

READ COMMITTED (读已提交模式)

Shell A Shell B
set session transaction isolation level read committed; set session transaction isolation level read committed;
start transaction; start transaction;
select * from accounts where id=1; select * from accounts where id=1;
结果是: ` 1
update accounts set balance=balance+100 where id=1;
结果是: Query OK, 1 row affected (0.01 sec)
select * from accounts where id=1; select * from accounts where id=1;
结果是: ` 1
commit;
select * from accounts where id=1; select * from accounts where id=1;
结果是: ` 1

这个模式中, 在 Shell B 的事务提交之前, Shell A 已经读取不到脏数据 了, 只有在 Shell B 提交后才能读取到 update 后的值

但是这还有一个问题: Shell A 在同一个事务中读取同一行的数据, 出现了不同的结果, 这是因为 Shell A 在执行事务期间, Shell B 的事务可以照常修改数据, 这种现象就叫 (Shell A)不可重复读

REPEATABLE READ (可重复读模式)

Shell A Shell B
set session transaction isolation level repeatable read; set session transaction isolation level repeatable read;
start transaction; start transaction;
select * from accounts where id=1; select * from accounts where id=1;
结果是: ` 1
update accounts set balance=balance+100 where id=1;
结果是: Query OK, 1 row affected (0.01 sec)
select * from accounts where id=1; select * from accounts where id=1;
结果是: ` 1
commit;
select * from accounts where id=1; select * from accounts where id=1;
结果是: ` 1

READ COMMITTED模式不同的是, 即便是 Shell B 中的提交了事务, Shell A 中查询出来的依然是1000的余额, 解决了 不可重复读 问题, 这是因为该模式下,一次性读 机制让事务中的每次 select 都返回事务中第一次select所建立的快照

另外, 在这个模式下, 即便其他事务对该事务所影响的行执行了删除操作, 在当前事务中, 依然能够看到被删除的数据

由于该模式还是允许其他事务修改数据, 所以可能出现一些非预期的情况, 下面这是官网的例子:

Shell A Shell B
set session transaction isolation level repeatable read; set session transaction isolation level repeatable read;
start transaction; start transaction;
select COUNT(c2) from t1 where c2 = 'abc';
结果: Returns 0: no rows match.
插入 10 条数据insert t1 values (...)
commit;
update t1 set c2 = 'cba' where c2 = 'abc';
select COUNT(c2) from t1 where c2 = 'cba';
结果: Returns 10

在这个例子中,Shell A 在 Shell B 提交后执行了update语句, 导致把 Shell B 提交的数据也更新了, 这个现象就叫做 幻读(Shell A 更新了事务开始时不存在的数据), 在实际开发中也是很危险的行为

SERIALIZABLE (串行模式)

Shell A Shell B
set session transaction isolation level serializable; set session transaction isolation level serializable;
start transaction; start transaction;
select count(id) from accounts';
结果: Returns 2
插入 1 条数据insert accounts values (...)
暂停执行, 等待 Shell A 提交
commit; 自动执行 insert 语句

从过程中可以看到, Shell B 中的 insert 语句被暂停执行了, 这是啥因为 serializable 模式下, 所有普通的select语句都被隐式添加了for share的共享锁: 当前事务开启后, 其他事务只能读取被锁定的行, 不能修改; 这个案例中由于执行了count()函数, 相当于是该表整个被锁定了, 所以 Shell B 也无法执行 insert语句

这个模式就解决了幻读的问题

一致性读(consistent read)

一致性读 是 InnoDB 在 READ COMMITTEDREPEATABLE READ 隔离级别下处理 select 语句的默认模式。一致性读不会对其访问的表设置任何锁,因此在对表执行一致性读的同时,其他会话可以自由地修改这些表。

READ COMMITTED模式下, 一致性读 保证事务中每次 select 都会读取并设置其自己的最新快照

而在REPEATABLE READ模式下, 一致性读 保证事务中每次 select 的结果都是相同的, 这是因为该模式下, 当发出一致读取(即普通的 SELECT 语句)时,InnoDB 会为所在事务提供一个时间点,后续事务内的查询将根据该时间点查看数据库

相关推荐
wear工程师3 分钟前
MySQL 索引条件下推是什么?Using index condition 并不是覆盖索引
sql·mysql
方便面不加香菜1 小时前
MySQL 复合查询
数据库·mysql
花花鱼1 小时前
MySQL Illegal mix of collations 全解|字符集与排序规则冲突原理、分层排错、通用解决方案(适配5.7/8.0迁移)
数据库·mysql
Lyyaoo.10 小时前
Spring Boot Validation 声明式参数校验
spring boot·后端·mysql
南城以南溫暖如初14712 小时前
外卖CPS实战指南:从技术选型到运营落地全流程解析
java·spring boot·mysql·架构·vue
冰之杍17 小时前
MySQL utf8mb3 → utf8mb4 完整修改方案
数据库·mysql
这个DBA有点耶17 小时前
一文讲透数据库分类:关系型、非关系型、OLTP、OLAP、分布式、多模……
数据库·mysql·架构
Oracle恢复实录18 小时前
SQL优化改IN躲开全表扫描性能提升53倍
mysql
leo_yu_yty19 小时前
Mysql 面试准备
数据库·mysql·面试
冰暮流星1 天前
mysql练习1
数据库·mysql