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

相关推荐
小兜全糖(xdqt)2 小时前
mysql数据同步到sql server
mysql·adb
Karoku0662 小时前
【企业级分布式系统】Zabbix监控系统与部署安装
运维·服务器·数据库·redis·mysql·zabbix
周全全3 小时前
MySQL报错解决:The user specified as a definer (‘root‘@‘%‘) does not exist
android·数据库·mysql
白云如幻3 小时前
MySQL的分组函数
数据库·mysql
秋意钟5 小时前
MySQL日期类型选择建议
数据库·mysql
ac-er88886 小时前
MySQL如何实现PHP输入安全
mysql·安全·php
桀桀桀桀桀桀6 小时前
数据库中的用户管理和权限管理
数据库·mysql
瓜牛_gn11 小时前
mysql特性
数据库·mysql
Yaml416 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
追风林17 小时前
mac 本地docker-mysql主从复制部署
mysql·macos·docker