【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)

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

总结

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

相关推荐
师太,答应老衲吧1 小时前
SQL实战训练之,力扣:2020. 无流量的帐户数(递归)
数据库·sql·leetcode
Yaml42 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
Channing Lewis2 小时前
salesforce case可以新建一个roll up 字段,统计出这个case下的email数量吗
数据库·salesforce
追风林2 小时前
mac 本地docker-mysql主从复制部署
mysql·macos·docker
毕业设计制作和分享3 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
ketil273 小时前
Redis - String 字符串
数据库·redis·缓存
Hsu_kk4 小时前
MySQL 批量删除海量数据的几种方法
数据库·mysql
编程学无止境4 小时前
第02章 MySQL环境搭建
数据库·mysql
knight-n4 小时前
MYSQL库的操作
数据库·mysql
包饭厅咸鱼5 小时前
QML----复制指定下标的ListModel数据
开发语言·数据库