【MySQL】MySQL事务的问题:脏读、幻读、不可重复读

MySQL事务的问题:脏读、幻读、不可重复读

在上一篇文章中,我们已经学习过了事务相关的基础知识,今天,我们继续学习事务有可能带来的一些问题。其实在一次请求和连接中,事务是不会出现什么问题的,毕竟在一个事务中,要么全提交,要么全回滚。但是如果有多个客户端连接,也就是说在并发操作事务的情况下,就会发生各种问题。其中最典型的就是下面三种情况,不过首先我们设置一下事务隔离级别,这个东西我们下次再讲,这回我们先把事务隔离级别设置为最低的级别。

go 复制代码
-- my.cnf
[server]
transaction-isolation = READ-UNCOMMITTED

脏读

脏读的意思就是两个事务同时在运行,其中 A 事务修改了某个字段,B 事务读取了这个字段,这时可能因为某种原因,A 事务的修改操作回滚了,那么 B 读取的数据就是不正确的,也就是说,B 读到的数据是 "脏" 的。

go 复制代码
-- 事务 A
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update test_user set username = 'aaa' where id = 2199993;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

-- 事务 B
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select username from test_user where id = 2199993;
+----------+
| username |
+----------+
| aaa      |
+----------+
1 row in set (0.00 sec)

在这个例子中,事务 A 没有提交,但事务 B 读取到的依然是修改之后的数据,之后如果事务 A 回滚或者再次修改成别的数据,那么 B 那边之前读取到的数据就一直是有问题的。

更加典型的例子是,假如我们修改的是你帐户里余额,本身余额有 100 块,第一笔交易将余额修改成了 80 块,这时第二个交易事务过来,读到了你的余额是 80 块,然后又扣了 10 块钱,正常情况下你的余额应该是 70 块了。但是,注意,第一笔交易的事务出现问题了,无法正常继续交易,于是回滚成 100 块。然而第二笔交易的事务并不知情,依然正常交易并提交了,这时你的余额就变成了 70 元。很明显,这就产生了问题,这个就是脏读带来的结果,一致性出现了问题。

不可重复读

不可重复读是啥意思呢?其实跟上面的是一样概念,只是说我们的 B 事务之后又读了一次 A 事务已经提交的数据,发现两次数据不对呀,这就是不可重复读。官方一点的解释就是:A 事务修改了数据,B 事务在修改前和修改后读取的数据不一样。注意,在不可重复读中,没有回滚的操作,另外,如果两个事务同时都是修改一条数据的话,那么后修改的数据会覆盖前面修改事务的操作结果,这也是不可重复读的问题。

go 复制代码
-- 事务B
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select username from test_user where id = 2199993;
+----------------------+
| username             |
+----------------------+
| 355607b664269a13dae9 |
+----------------------+
1 row in set (0.00 sec)

-- 事务A
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update test_user set username = 'aaa' where id = 2199993;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;
Query OK, 1 row affected (0.00 sec)

-- 事务B
mysql> select username from test_user where id = 2199993;
+----------+
| username |
+----------+
| aaa      |
+----------+
1 row in set (0.00 sec)

幻读

最后一个幻读,其实它和前面两个问题的情况也是类似的,都是读取的不一致问题,并且和不可重复读非常类似。当 B 事务读取数据时使用的是聚合方式,比如说查询数量,那么假设 A 事务在 B 事务第一次读取后增加或者删除了数据,那么 B 事务第二次读取的时候这个数量就会发生变化,就好像产生幻觉了一样。

go 复制代码
-- 事务B第一次读取
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select count(*) from test_user;
+----------+
| count(*) |
+----------+
|  2200001 |
+----------+
1 row in set (0.49 sec)

-- 事务A删除
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from test_user where id < 10;
Query OK, 9 rows affected (0.00 sec)

-- 事务B再次读取
mysql> select count(*) from test_user;
+----------+
| count(*) |
+----------+
|  2199992 |
+----------+
1 row in set (0.37 sec)

咋一看,幻读和不可重复读貌似是一个意思呀?确实,它们非常类似,但是,幻读更强调的是聚合操作结果,而不是单一一条数据的修改,这就是它们两个之间最本质的区别。

总结

好了,问题呈现在眼前了,其实大家应该能看出,事务常见的这三个问题都和数据的一致性读取有关,也就是说,在多个并发事务的前提下,如何保证数据的并发一致性就是我们要面对的问题。那么这些问题是怎么解决的呢?这个就是我们下回要讲到的内容了,也就是 事务隔离 机制相关的知识。

相关推荐
IT项目管理30 分钟前
达梦数据库DMHS介绍及安装部署
linux·数据库
你都会上树?37 分钟前
MySQL MVCC 详解
数据库·mysql
大春儿的试验田43 分钟前
高并发收藏功能设计:Redis异步同步与定时补偿机制详解
java·数据库·redis·学习·缓存
Ein hübscher Kerl.1 小时前
虚拟机上安装 MariaDB 及依赖包
数据库·mariadb
长征coder1 小时前
AWS MySQL 读写分离配置指南
mysql·云计算·aws
醇醛酸醚酮酯2 小时前
Qt项目锻炼——TODO清单(二)
开发语言·数据库·qt
ladymorgana2 小时前
【docker】修改 MySQL 密码后 Navicat 仍能用原密码连接
mysql·adb·docker
PanZonghui2 小时前
Centos项目部署之安装数据库MySQL8
linux·后端·mysql
GreatSQL社区2 小时前
用systemd管理GreatSQL服务详解
数据库·mysql·greatsql
掘根2 小时前
【MySQL进阶】错误日志,二进制日志,mysql系统库
数据库·mysql