MySQL8.0 查看默认事务的隔离级别,并模拟测试

文章目录

    • 3. MySQL8.0 查看默认事务的隔离级别,并测试
      • 3.1 更改隔离级别为:未提交读测试
      • 3.2 测试隔离级别为:提交读
      • 3.3 测试可重复读
        • 3.3.1 模拟幻读
      • 3.4 可串行化就不测试了,就是顺序的问题,谁的事务先开启谁进行,其它的都要等待

3. MySQL8.0 查看默认事务的隔离级别,并测试

如下:MySQL默认是:可重复读

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

3.1 更改隔离级别为:未提交读测试

两个命令行客户端分别为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)

  1. 准一张表,测试事务
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
  1. 窗口1: 启动事务A,数据库3条记录是初始状态
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)
  1. 窗口2. 启动事务B ,修改数据,但是不提交事务B
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)
  1. 窗口1 事务A 查看数据是否已经被修改
    发现已经被修改,这就是所谓的“脏读”
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
|  1 |   1 |
|  2 |   2 |
|  3 |  33 |
+----+-----+
3 rows in set (0.00 sec)
  1. 窗口2 回滚事务B
mysql> rollback ;
Query OK, 0 rows affected (0.04 sec)
  1. 窗口1 事务A查看数据是否回滚
    发现已经回滚
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
|  1 |   1 |
|  2 |   2 |
|  3 |   3 |
+----+-----+
3 rows in set (0.00 sec)
  1. 经过上面的实验可以得出结论,事务B更新了一条记录,但是没有提交,此时事务A可以查询出未提交记录。造成脏读现象。未提交读是最低的隔离级别

3.2 测试隔离级别为:提交读

把窗口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)

  1. 窗口A开启事务,查询
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)
  1. 窗口2开启事务,修改数据,并查看
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)
  1. 窗口A再次查看数据有没有被更新
    发现没有,隔离级别生效
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
|  1 |   1 |
|  2 |   2 |
|  3 |   3 |
+----+-----+
3 rows in set (0.00 sec)
  1. 窗口B 提交事务
mysql> commit ;
Query OK, 0 rows affected (0.08 sec)
  1. 窗口A 再次查询数据
    发现数据被修改了,符合预期
mysql> select * from test;
+----+-----+
| id | num |
+----+-----+
|  1 |   1 |
|  2 |  22 |
|  3 |   3 |
+----+-----+
3 rows in set (0.00 sec

看是正常的代码,但是这个时候有什么问题?
有:在事务A中,两次读取的数据不一致,即不可重复读

  1. 经过上面的实验可以得出结论,已提交读隔离级别解决了脏读的问题,但是出现了不可重复读的问题,即事务A在两次查询的数据不一致,因为在两次查询之间事务B更新了一条数据。已提交读只允许读取已提交的记录,但不要求可重复读。

3.3 测试可重复读

  1. 事务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)
  1. 事务B查看事务,默认的即可
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)
  1. 窗口A 查看发现没有被修改,符合预期
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
|  1 |   1 |
|  2 |  22 |
|  3 |   3 |
+----+-----+
3 rows in set (0.00 sec)
  1. 窗口B提交事务并查看,发现数据已经被更新了
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)

  1. 窗口A查看数据
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
|  1 |   1 |
|  2 |  22 |
|  3 |   3 |
+----+-----+
3 rows in set (0.00 sec)

发现数据依然没有被修改,符合可重复读的预期,即在一个事务内读取一个值的数据始终应该保持一致
但是!!!
有什么问题没有!!!——————》》数据不是最新的数据

  1. 如果我 再次开启事务并提交事务在窗口B插入一条记录呢?
    下面在B窗口中,但是不开启事务
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)
  1. 窗口A 再次查看,发现还是没有获取到最新数据
mysql> select * from test ;
+----+-----+
| id | num |
+----+-----+
|  1 |   1 |
|  2 |  22 |
|  3 |   3 |
+----+-----+
3 rows in set (0.00 sec

  1. 把事务A提交了,获取数据,才发现是最新的
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是看不到的。但该事务不要求与其他事务可串行化。例如,当一个事务可以找到由一个已提交事务更新的记录,但是可能产生幻读问题(注意是可能,因为数据库对隔离级别的实现有所差别)。像以上的实验,就没有出现数据幻读的问题。

3.3.1 模拟幻读

上述的实验没有出现幻读的现象,下面我们来模拟一下什么是幻读?

  1. 保证两个窗口AB的隔离级别:都是可重复读
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)
  1. 在A窗口开启事务,查询
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)

  1. 在B窗口不开启事务直接插入一条记录,num=7
mysql> insert into test (num) values (7);
Query OK, 1 row affected (0.10 sec)
  1. 在A窗口查询,是否有num=7的记录
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的记录,验证了可重复读的特性,符合预期,但是我们怎么模拟幻读呢?

  1. 在窗口A中添加一条记录,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条就产生了幻读。下面验证一下

  1. 在窗口A中更新num=7的记录,并再次查询
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条,所以出现了幻读


3.4 可串行化就不测试了,就是顺序的问题,谁的事务先开启谁进行,其它的都要等待

你可能感兴趣的:(MySql)