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

相关推荐
武子康1 分钟前
大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本
java·大数据·数据仓库·hive·hadoop·mysql
黑色叉腰丶大魔王6 分钟前
《MySQL 数据库备份与恢复》
mysql
Ljw...14 分钟前
索引(MySQL)
数据库·mysql·索引
OpsEye43 分钟前
MySQL 8.0.40版本自动升级异常的预警提示
数据库·mysql·数据库升级
Ljw...43 分钟前
表的增删改查(MySQL)
数据库·后端·mysql·表的增删查改
i道i8 小时前
MySQL win安装 和 pymysql使用示例
数据库·mysql
Oak Zhang10 小时前
sharding-jdbc自定义分片算法,表对应关系存储在mysql中,缓存到redis或者本地
redis·mysql·缓存
久醉不在酒11 小时前
MySQL数据库运维及集群搭建
运维·数据库·mysql
WindFutrue11 小时前
使用Mybatis向Mysql中的插入Point类型的数据全方位解析
数据库·mysql·mybatis
一只爱撸猫的程序猿12 小时前
一个简单的Linux 服务器性能优化案例
linux·mysql·nginx