解读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 会为所在事务提供一个时间点,后续事务内的查询将根据该时间点查看数据库

相关推荐
ob熔天使——武4 小时前
MySQL
数据库·mysql
野生技术架构师8 小时前
MySQL数据实时同步到Elasticsearch的高效解决方案
数据库·mysql·elasticsearch
焦虑的二狗12 小时前
Mac下载mysql
数据库·mysql·macos
weixin_4569042712 小时前
控制台打开mysql服务报错解决办法
数据库·mysql
绅士范er13 小时前
【mysql 的安装及使用】
mysql
C1829818257515 小时前
幻想读 通过多版本并发控制(MVCC)和间隙锁(Gap Lock)的组合也能防止幻读具体说下
mysql
鲁子狄18 小时前
[笔记] 动态 SQL 查询技术解析:构建灵活高效的企业级数据访问层
java·spring boot·笔记·sql·mysql·mybatis
Dubhehug19 小时前
8.数据库索引
数据库·mysql·索引·索引分类·索引优缺点
冬夜戏雪19 小时前
阿里云ubuntu安装mysql docker容器(拉,运行,测试完整版)
mysql·ubuntu·阿里云
楼兰胡杨19 小时前
MySQL 更新字段的值为当前最大值加1
mysql