测试的mysql的版本号:
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.11 |
+-----------+
1 row in set (0.02 sec)
随便创建一个表:比如就是my_test,字段有三个,id(int),name(varchar),age(int)
CREATE TABLE `my_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`age` int(11) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
mysql 默认的事务级别为repeatable read (可以重复读)
查看当前mysql全局的事务级别:
select @@global.TRANSACTION_isolation
mysql> select @@global.TRANSACTION_isolation;
+--------------------------------+
| @@global.TRANSACTION_isolation |
+--------------------------------+
| REPEATABLE-READ |
+--------------------------------+
1 row in set (0.00 sec)
修改全局的事务级别:
set GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
关闭事务的主动提交: set global autocommit=off
记住global的设置后,重新开启的终端才起作用
此时查看:
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL read uncommitted;
Query OK, 0 rows affected (0.00 sec)
// 记住global的设置后,重新开启的终端才起作用,查看隔离级别
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED |
+-------------------------+
1 row in set (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED |
+-------------------------+
1 row in set (0.00 sec)
查看表中的数据:
mysql> select * from my_test;
+----+----------+------+
| id | name | age |
+----+----------+------+
| 1 | jeffchan | 20 |
| 2 | caraliu | 18 |
+----+----------+------+
2 rows in set (0.00 sec)
1、当事务级别是 READ UNCOMMITTED (有脏读,幻读,不可重复读)
现在开启mysql,然后打开两个操作界面:
那么就有两个需要自己手动提交的事务了:记录为A , B
两个终端同时开启事务,命令上面的数字为执行的顺序:
A事务:
1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
4
mysql> update my_test set name="jeffchan1" where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
5
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan1 | 20 |
| 2 | caraliu | 18 |
+----+-----------+------+
2 rows in set (0.00 sec)
=========================================================================================
B事务:
2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
3
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan | 20 |
| 2 | caraliu | 18 |
+----+-----------+------+
2 rows in set (0.00 sec)
6
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan1 | 20 |
| 2 | caraliu | 18 |
+----+-----------+------+
2 rows in set (0.00 sec)
A: 现在在事务A中修改表的记录:update my_test set name="jeffchan1" where id = 1
B: 此时起始事务还是没有提交,但是现在在B事务中:select * from my_test 会发现id=1的那条记录的name的值给改了,为
jeffchan1,这就是所谓的脏读(因为有可能事务A失败回滚了,此时事务B这个值其实时无效的,就是错误的值)
因为读未提交是最低级别的隔离,所以脏读(读取到未提交的数据)会存在(因为这个级别是基本不用的),不可重复读也存在,幻读也会存在。
2、当事务级别时: READ COMMITTED (解决了脏读问题,幻读和不可重复读还是有)
set GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED
设置完后,一定记得要重启开启新的终端。
A终端 :
1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
3
mysql> select * from my_test;
+----+----------+------+
| id | name | age |
+----+----------+------+
| 1 | jeffchan | 20 |
| 2 | caraliu | 18 |
+----+----------+------+
2 rows in set (0.00 sec)
mysql> update my_test set name="jeffchan2" where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
4
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan2 | 20 |
| 2 | caraliu | 18 |
+----+-----------+------+
2 rows in set (0.00 sec)
6
mysql> commit;
Query OK, 0 rows affected (0.16 sec)
=========================================================================================
B终端:
2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
5
mysql> select * from my_test;
+----+----------+------+
| id | name | age |
+----+----------+------+
| 1 | jeffchan | 20 |
| 2 | caraliu | 18 |
+----+----------+------+
2 rows in set (0.00 sec)
A终端提交事务:此时查看发现修改了
6
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan2 | 20 |
| 2 | caraliu | 18 |
+----+-----------+------+
2 rows in set (0.00 sec)
事务A: update my_test set name="jaychou1" where id = 2 ,不提交事务
事务B: select * from my_test 发现id=2的数据没有被修改,所以已经没有脏读了,但是此时你将事务A提交,此时
再进行 select * from my_test 发现id=2的数据被修改了。也是同一个事务中两次读的数据是不一样的,这个叫不可以重复读,不 可以在一个事务中重复读某个记录,可能会出现不一致现象),此时幻读存在。
3、当事务级别时: REPEATABLE READ (解决了脏读问题和不可重复读问题,幻读还是存在)
set GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ
设置完后,一定记得要重启开启新的终端。
A事务:
1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
3
mysql> update my_test set name="jeffchan3" where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
4
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan3 | 20 |
| 2 | caraliu | 18 |
| 3 | hello | 12 |
+----+-----------+------+
3 rows in set (0.00 sec)
5
mysql> commit;
Query OK, 0 rows affected (0.16 sec)
=========================================================================================
B事务:
2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
3
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan2 | 20 |
| 2 | caraliu | 18 |
| 3 | hello | 12 |
+----+-----------+------+
3 rows in set (0.00 sec)
6
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan2 | 20 |
| 2 | caraliu | 18 |
| 3 | hello | 12 |
+----+-----------+------+
3 rows in set (0.00 sec)
很明显A事务修改(我试了下,插入和修改也是一样的结果)记录的时候,不管是否提交事务,B事务都不会读到A事务中修改的记录,所以不管B事务在事务中重复多少次读取记录,都是一样的,所以叫可以重复读,在读取数据的层面,不会出现幻读的情况。
但是update记录时,不做条件限制的话,会update到别的事务新插入的其他记录,当前事务查看时,没看到那条记录,其实它已经存在了,然后做范围修改的时候,会修改到它,好像出现幻觉一样。每一个命令上面加一个执行的顺序。
A事务:
1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
3
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan3 | 20 |
| 2 | caraliu | 18 |
| 3 | hello | 13 |
+----+-----------+------+
3 rows in set (0.00 sec)
6
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan3 | 20 |
| 2 | caraliu | 18 |
| 3 | hello | 13 |
+----+-----------+------+
3 rows in set (0.00 sec)
8
mysql> update my_test set age = 40;
Query OK, 4 rows affected (0.00 sec)
Rows matched: 4 Changed: 4 Warnings: 0
9
mysql> commit;
Query OK, 0 rows affected (0.08 sec)
10
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan3 | 40 |
| 2 | caraliu | 40 |
| 3 | hello | 40 |
| 4 | hi | 40 |
+----+-----------+------+
4 rows in set (0.00 sec)
=============================================================================================
B事务:
2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
4
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan3 | 20 |
| 2 | caraliu | 18 |
| 3 | hello | 13 |
+----+-----------+------+
3 rows in set (0.00 sec)
5
mysql> insert into my_test values(4,"hi",30);
Query OK, 1 row affected (0.00 sec)
7
mysql> commit;
Query OK, 0 rows affected (0.17 sec)
可以看到当执行到第10步的时候,查出来的结果,事务B新插入的记录也被修改了。所以幻读依然存在。
4、当事务级别时: SERIALIZABLE (不存在并发问题,但是效率很低)
set GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE
设置完后,一定记得要重启开启新的终端。
A事务:
1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
5 这一步会阻塞
mysql> select * from my_test;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
==========================================================================================
B事务:
2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
3
mysql> select * from my_test;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | jeffchan3 | 40 |
| 2 | caraliu | 40 |
| 3 | hello | 40 |
| 4 | hi | 40 |
+----+-----------+------+
4 rows in set (0.00 sec)
4
mysql> insert into my_test values(5,"good",13);
Query OK, 1 row affected (0.00 sec)
发现在第五步的时候,会阻塞查询的指令,所以没有并发问题,但是会很消耗性能,所以也是基本不使用的隔离级别