Mysql 四種事務隔離級別
一、前提
時過一年重新拾起博文記錄,希望後面都能堅持下來。 接著之前MySql的學習,先記錄下這篇。
以下都是基於mysql8 innodb存儲引擎進行分析的。
二、事務的ACID特性
-
A(Atomicity) 原子性
-
C(consistency) 一致性
-
I(isolation) 隔離性
-
D(durability) 持久性
三、事務的4種隔離級別
-
Read Uncommitted - 未提交讀
| 步驟 | 事務1 | 事務2 |
| 1 |
設置隔離級別 mysql> set @@session.transaction_isolation |
|
| 2 |
開啟事務1 mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from t; Empty set (0.00 sec) |
|
| 3 |
無需管隔離級別,只開啟事務2並插入記錄
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> insert into t select 1, '1'; Query OK, 1 row affected (0.00 sec) Records: 1 Duplicates: 0 Warnings: 0 |
|
| 4 |
此時能查到事務2中未提交事務中的數據,這就是臟讀。 mysql> select * from t; +------+------+ | id | name | +------+------+ | 1 | 1 | +------+------+ 1 row in set (0.00 sec) |
1 mysql> show variables like '%isolation%'; 2 +-----------------------+------------------+ 3 | Variable_name | Value | 4 +-----------------------+------------------+ 5 | transaction_isolation | READ-UNCOMMITTED | 6 +-----------------------+------------------+ 7 1 row in set (0.00 sec)
-
Read Committed - 提交讀
| 步驟 | 事務1 | 事務2 |
| 1 | 先設置隔離級別
mysql> set @@session.transaction_isolation |
|
| 2 | 開啟事務1,查詢t表記錄為空
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from t; Empty set (0.00 sec) |
|
| 3 |
無需管隔離級別,只開啟事務2並插入記錄 mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> insert into t select 2, '2'; Query OK, 1 row affected (0.01 sec) Records: 1 Duplicates: 0 Warnings: 0 |
|
| 4 |
繼續查詢表t依然顯示為空 (沒有讀取到事務2未提交的數據,所以不存在臟讀問題) mysql> select * from t; Empty set (0.00 sec) |
|
| 5 |
提交事務2 mysql> commit; |
|
| 6 |
繼續查詢表t,此時能查詢事務2已提交的數據記錄(出現前後查詢不一致) mysql> select * from t; +------+------+ | id | name | +------+------+ | 2 | 2 | +------+------+ |
-
Repeatable Read - 可重複讀
| 步驟 | 事務1 | 事務2 |
| 1 |
設置隔離級別 mysql> set @@session.transaction_isolation |
|
| 2 |
開啟事務1,並查詢 mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from t; +------+------+ | id | name | +------+------+ | 3 | 3 | +------+------+ |
|
| 3 |
無需管隔離級別,只開啟事務2並插入記錄 mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> insert into t select 4, '4'; Query OK, 1 row affected (0.01 sec) Records: 1 Duplicates: 0 Warnings: 0 |
|
| 4 | 繼續查詢表t依然只有一條記錄id=3
(沒有讀取到事務2未提交的數據,所以不存在臟讀問題) mysql> select * from t; +------+------+ | id | name | +------+------+ | 3 | 3 | +------+------+ |
|
| 5 |
將第二個窗口中的事務提交。
mysql> commit; |
|
| 6 | 繼續查詢表t依然只有一條記錄id=3
(沒有讀取到事務2已提交的數據,所以不存在不可重複讀問題) mysql> select * from t; +------+------+ | id | name | +------+------+ | 3 | 3 | +------+------+ |
|
| 7 |
接著插入一條4的記錄,但提示插入不了,提示主鍵衝突問題。
然而查詢卻沒有這條id=4記錄。 這就是幻讀現象。
mysql> insert into t select 4, '4'; ERROR 1062 (23000): Duplicate entry '4' for key 'PRIMARY' mysql> select * from t; +----+------+ | id | name | +----+------+ | 3 | 3 | +----+------+ 1 row in set (0.00 sec) |
|
| 8 | 另一種幻讀現象:接著上面操作,不插入記錄只更新記錄,將name 更新成 '5'後,發現有兩行受影響 | |
mysql> update t set name = '5'; Query OK, 2 rows affected (0.00 sec) Rows matched: 2 Changed: 2 Warnings: 0 mysql> select * from t; +----+------+ | id | name | +----+------+ | 3 | 5 | | 4 | 5 | +----+------+ |
||
| 步驟 | 事務1 | 事務2 |
| 9 |
查詢時加上間隙鎖 mysql> begin; mysql> select * from t where id > 0 for update; +----+------+ | id | name | +----+------+ | 3 | 5 | | 4 | 5 | +----+------+
|
|
| 10 |
插入記錄為6的數據就會發現插入這條記錄獲取鎖超時,自動異常 insert into t select 6, '6'; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
|
-
Serializable - 可串列化
四 總結
1、臟讀:在一個事務中會讀取到其未提交事務的數據,此種現象也稱之為臟讀
2、不可重複讀:一個事務可以讀取另一個已提交的事務,多次讀取會造成不一樣的結果。這種現象也被稱為不可重複讀
3、幻讀:基於可重複讀的基礎上查詢結果是一樣的,但是當對某些行進行更新或者插入時卻會受到影響操作不了,就形成了幻讀。
|
隔離級別
|
臟讀
|
不可重複讀
|
幻讀
|
|
讀未提交(uncommitted read)
|
可能出現
|
可能出現
|
可能出現
|
|
讀提交(committed read)
|
不會出現
|
可能出現
|
可能出現
|
|
可重複讀(Repeatable Read)
|
不會出現
|
不會出現
|
可能出現(加上間隙鎖就不會)
|
|
可串列化(Serializable)
|
不會出現
|
不會出現
|
不會出現
|
五 參考文獻
《MySql 技術內幕(Innodb)第二版》
//dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html
