MySQL默认事务隔离级别引起的重复读问题

遇到的问题

现在有两个方法,都加了@Transactional,分别在各自的事务中执行
方法一:插入一条数据
方法二:循环查询刚刚插入的数据,如果查到了数据则跳出循环

原因分析

查看MySQL事物级别:

SELECT @@tx_isolation;
//mysql8.0 以上版本的查询
select @@transaction_isolation;

查询结果是 repeatable read(可重复读)

mysql> SELECT @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| REPEATABLE-READ|
+----------------+
1 row in set (0.01 sec)

mysql安装的时候没有指定事务隔离级别默认是repeatable read(可重复读),
这样当你在同一个事务中循环执行SQL,每次都会读取到第一次执行的数据

解决办法

将MySQL事务级别改为 read committed(已提交读) ,问题解决

set global transaction isolation level read committed;

做个测试重现可重复读问题

将事务隔离级别设置为repeatable read,开始事务,查询表中的数据,此时表中的score=87

mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+
1 row in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from user_score where id = 1;
+----+--------+-------+--------+
| id | name   | score | course |
+----+--------+-------+--------+
|  1 | 张三 | 87    | 语文 |
+----+--------+-------+--------+
1 row in set (0.00 sec)

另外开一个窗口,更新这条数据的score=86

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> update user_score set score = 86 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from user_score where id = 1;
+----+--------+-------+--------+
| id | name   | score | course |
+----+--------+-------+--------+
|  1 | 张三 | 86    | 语文 |
+----+--------+-------+--------+
1 row in set (0.00 sec)

返回第一个窗口查询数据,此时第一个窗口还在之前的事务中,无法查询到已经更改的数据

mysql> select * from user_score where id = 1;
+----+--------+-------+--------+
| id | name   | score | course |
+----+--------+-------+--------+
|  1 | 张三 | 87    | 语文 |
+----+--------+-------+--------+
1 row in set (0.08 sec)

提交事务再次查询就可以查询到数据了

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from user_score where id = 1;
+----+--------+-------+--------+
| id | name   | score | course |
+----+--------+-------+--------+
|  1 | 张三 | 86    | 语文 |
+----+--------+-------+--------+
1 row in set (0.01 sec)

设置隔离级别为read committed(已提交读)
再重复之前查询的步骤,会发现第一个窗口中不管事务有没有提交,都可以查询到第二个窗口中更新的已提交事务的数据

mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-COMMITTED          |
+-------------------------+
1 row in set (0.00 sec)
………………
…………
……

拓展学习

转自https://www.cnblogs.com/ryelqy/p/11434120.html

事务的隔离级别分为一下四种:
read uncommitted(未提交读)

A事务已执行,但未提交;B事务查询到A事务的更新后数据;A事务回滚;—出现脏数据

read committed(已提交读)

A事务执行更新;B事务查询;A事务又执行更新;B事务再次查询时,前后两次数据不一致;—不可重复读

repeatable read(可重复读)

A事务无论执行多少次,只要不提交,B事务查询值都不变;B事务仅查询B事务开始时那一瞬间的数据快照;

serializable(串行化)

不允许读写并发操作,写执行时,读必须等待;

修改隔离级别
//查看当前事物级别:
SELECT @@tx_isolation;
//设置read uncommitted级别:
set global transaction isolation level read uncommitted;
//设置read committed级别:
set global transaction isolation level read committed;
//设置repeatable read级别:
set global transaction isolation level repeatable read;
//设置serializable级别:
set global transaction isolation level serializable;

发表评论

邮箱地址不会被公开。 必填项已用*标注