如下:MySQL默认是:可重复读
mysql> select @@global.transaction_isolation,@@transaction_isolation;
+--------------------------------+-------------------------+
| @@global.transaction_isolation | @@transaction_isolation |
+--------------------------------+-------------------------+
| REPEATABLE-READ | REPEATABLE-READ |
+--------------------------------+-------------------------+
1 row in set (0.00 sec)
两个命令行客户端分别为A,B;不断改变A的隔离级别,在B端修改数据。
但是不改变B的隔离级别,只改变A的,B还是默认的隔离级别:可重复读
A:
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
## 查看
mysql> select @@global.transaction_isolation,@@transaction_isolation;
+--------------------------------+-------------------------+
| @@global.transaction_isolation | @@transaction_isolation |
+--------------------------------+-------------------------+
| REPEATABLE-READ | READ-UNCOMMITTED |
+--------------------------------+-------------------------+
1 row in set (0.00 sec)
CREATE TABLE `test` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`num` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
## 插入3条数据
INSERT INTO `trans`.`test` (`id`, `num`) VALUES ('1', '1');
INSERT INTO `trans`.`test` (`id`, `num`) VALUES ('2', '2');
INSERT INTO `trans`.`test` (`id`, `num`) VALUES ('3', '3');
用两个窗口分别打开MySQL
mysql -uroot -p123456
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update test set num =33 where id =3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
## 在事务B内已经修改了
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 33 |
+----+-----+
3 rows in set (0.00 sec)
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 33 |
+----+-----+
3 rows in set (0.00 sec)
mysql> rollback ;
Query OK, 0 rows affected (0.04 sec)
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
把窗口A的隔离级别换为:提交读,窗口B的不换还是默认的
mysql> set session transaction isolation level read committed ;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@global.transaction_isolation,@@transaction_isolation;
+--------------------------------+-------------------------+
| @@global.transaction_isolation | @@transaction_isolation |
+--------------------------------+-------------------------+
| REPEATABLE-READ | READ-COMMITTED |
+--------------------------------+-------------------------+
1 row in set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update test set num =22 where id =2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 22 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
mysql> commit ;
Query OK, 0 rows affected (0.08 sec)
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 22 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec
看是正常的代码,但是这个时候有什么问题?
有:在事务A中,两次读取的数据不一致,即不可重复读
mysql> set session transaction isolation level repeatable read ;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@global.transaction_isolation,@@transaction_isolation;
+--------------------------------+-------------------------+
| @@global.transaction_isolation | @@transaction_isolation |
+--------------------------------+-------------------------+
| REPEATABLE-READ | REPEATABLE-READ |
+--------------------------------+-------------------------+
1 row in set (0.00 sec)
## 查看数据
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 22 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
mysql> select @@global.transaction_isolation,@@transaction_isolation;
+--------------------------------+-------------------------+
| @@global.transaction_isolation | @@transaction_isolation |
+--------------------------------+-------------------------+
| REPEATABLE-READ | REPEATABLE-READ |
+--------------------------------+-------------------------+
1 row in set (0.00 sec)
## 事务B开启事务并修改id=1的数据
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 22 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
mysql> update test set num =11 where id =1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
| 1 | 11 |
| 2 | 22 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 22 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
mysql> commit ;
Query OK, 0 rows affected (0.08 sec)
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
| 1 | 11 |
| 2 | 22 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 22 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
发现数据依然没有被修改,符合可重复读的预期,即在一个事务内读取一个值的数据始终应该保持一致
但是!!!
有什么问题没有!!!——————》》数据不是最新的数据
mysql> start transaction;
mysql> insert into test (num) value(4);
Query OK, 1 row affected (0.07 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
| 1 | 11 |
| 2 | 22 |
| 3 | 3 |
| 4 | 4 |
+----+-----+
4 rows in set (0.00 sec)
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 22 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
| 1 | 11 |
| 2 | 22 |
| 3 | 3 |
| 4 | 4 |
+----+-----+
4 rows in set (0.00 sec
由以上的实验可以得出结论,可重复读隔离级别只允许读取已提交记录,而且在一个事务A两次读取一个记录期间,其他事务B如果更新该记录,事务A是看不到的。但该事务不要求与其他事务可串行化。例如,当一个事务可以找到由一个已提交事务更新的记录,但是可能产生幻读问题(注意是可能,因为数据库对隔离级别的实现有所差别)。像以上的实验,就没有出现数据幻读的问题。
上述的实验没有出现幻读的现象,下面我们来模拟一下什么是幻读?
select @@global.transaction_isolation,@@transaction_isolation;
+--------------------------------+-------------------------+
| @@global.transaction_isolation | @@transaction_isolation |
+--------------------------------+-------------------------+
| REPEATABLE-READ | REPEATABLE-READ |
+--------------------------------+-------------------------+
## 假设现在已经有了6条记录分别是
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
| 1 | 11 |
| 2 | 22 |
| 3 | 3 |
| 4 | 4 |
| 5 | 55 |
| 6 | 55 |
+----+-----+
6 rows in set (0.00 sec)
mysql> start transaction ;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
| 1 | 11 |
| 2 | 22 |
| 3 | 3 |
| 4 | 4 |
| 5 | 55 |
| 6 | 55 |
+----+-----+
6 rows in set (0.00 sec)
mysql> insert into test (num) values (7);
Query OK, 1 row affected (0.10 sec)
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
| 1 | 11 |
| 2 | 22 |
| 3 | 3 |
| 4 | 4 |
| 5 | 55 |
| 6 | 55 |
+----+-----+
6 rows in set (0.00 sec)
发现没有Num=7的记录,验证了可重复读的特性,符合预期,但是我们怎么模拟幻读呢?
mysql> insert into test (num) values (7);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
| 1 | 11 |
| 2 | 22 |
| 3 | 3 |
| 4 | 4 |
| 5 | 55 |
| 6 | 55 |
| 8 | 7 |
+----+-----+
7 rows in set (0.00 sec)
发现直接跳过了id=7的数据,也说明了肯定有其它的线程在操作,试想我们如果更新num=7的记录会更新几条呢?如果更新了1条就不是幻读,如果更新了2条就产生了幻读。下面验证一下
mysql> update test set num =777 where num =7;
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
| 1 | 11 |
| 2 | 22 |
| 3 | 3 |
| 4 | 4 |
| 5 | 55 |
| 6 | 55 |
| 7 | 777 |
| 8 | 777 |
+----+-----+
8 rows in set (0.00 sec)
哇!!! 结果匹配了两条,而且查询出来多了一条记录,这难道是幻读????YES
,这就是幻读
模拟成功,类似删除也有类似效果,我们不再模拟
大致思路是:在A窗口开启事务,此时查询到两条777的记录,在B窗口新添加一条记录num =777,再加上原来两条777的记录公有三条,此时在A窗口执行删除语句
mysql> delete from test where num =777;
Query OK, 3 rows affected (0.00 sec)
发现删除了3条,但是预期是2条,所以出现了幻读