測試的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)
發現在第五步的時候,會阻塞查詢的指令,所以沒有併發問題,但是會很消耗性能,所以也是基本不使用的隔離級別