遇到的问题
现在有两个方法,都加了@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;