Mysql四種事務隔離級別
先了解一下事務的四大特性:ACID
原子性(Atomicity)
原子性就是不可拆分的特性,要麼全部成功然後提交(commit),要麼全部失敗然後回滾(rollback)。MySQL通過Redo Log重做日誌實現了原子性,在將執行SQL語句時,會先寫入redo log buffer,再執行SQL語句,若SQL語句執行出錯就會根據redo log buffer中的記錄來執行回滾操作,由此擁有原子性。
一致性(Consistency)
一致性指事務將資料庫從一種狀態轉變為下一種一致的狀態。比如有一個欄位name有唯一索引約束,那麼在事務前後都不能有重複的name出現違反唯一索引約束,否則回滾。MySQL通過undo Log實現一致性,執行SQL語句時,會先寫入undo log再寫入 redo log buffer。undo是邏輯日誌,會根據之前的SQL語句進行相應回滾,並且除了回滾,undo log還有一個作用是MVCC,當用戶讀取一行記錄時,若該記錄已經被其他事務佔用,當前事務可通過undo讀取之前的行版本資訊,實現非鎖定讀取。並且undo log也會產生redo log,因為undo log也需要永續性的保護。
隔離性(Isolation)
如果沒有隔離性會發生如下問題:
1,資料丟失:A事務撤銷時,把已經提交的B事務的更新資料覆蓋了。
2,髒讀:髒讀主要是讀取到了其他事務的資料,而其他事務隨後發生回滾。MySQL通過三級封鎖協議的第二級解決了髒讀,在一級的基礎上,要求讀取資料 A 時必須加 S 鎖,讀取完馬上釋放 S 鎖。
3,不可重複讀:不可重複讀是讀取到資料後,隨後其他事務對資料發生了修改,無法再次讀取。MySQL通過三級封鎖協議的第三級解決了不可重複讀。在二級的基礎上,要求讀取資料 A 時必須加 S 鎖,直到事務結束了才能釋放 S 鎖。
4,幻讀:幻讀是讀取到資料後,隨後其他事務對資料發生了新增,無法再次讀取。在InnoDB引擎Repeatable Read的隔離級別下,MySQL通過Next-Key Lock以及MVCC解決了幻讀,事務中分為當前讀以及快照讀。
快照讀(snapshot read) ——通過MVCC來避免幻讀
簡單的select操作(不包括 select … lock in share mode, select … for update)
當前讀(current read) ——通過Next-Key Lock 來避免幻讀 Next-Key Lock即間隙鎖(Gap Lock)+行鎖 (Record Lock);
永續性(Durability)
一旦事務提交,則其所做的修改就會永久儲存到資料庫中。此時即使系統崩潰,修改的資料也不會丟失。具體實現原理就是在事務commit之前會將,redo log buffer中的資料持久化到硬碟中的redo log file,這樣在commit的時候,硬碟中已經有了我們修改或新增的資料,由此做到持久化。
Mysql的四大隔離級別
區別
Mysql預設隔離級別REPEATABLE-READ
演示四大隔離級別
建表語句
DROP TABLE IF EXISTS `user_account`;
CREATE TABLE `user_account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`balance` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
當前環境和版本
讀未提交read-uncommit
開啟一個客戶端A,並設定當前事務模式為read uncommitted(未提交讀),查詢表user_account的初始值:
在客戶端A的事務提交之前,開啟另一個客戶端B,更新表user_account:
此時在回到客戶端A查詢資料結果為:
這時候客戶端B事務還沒有提交,然後客戶端A就能查到資料客戶端B更新的資料了。那麼一旦客戶端B執行(ROLLBACK)回滾了事務,那麼此時就會導致客戶端A的資料髒讀。
提交已讀(read-commit)
開啟一個客戶端A,並設定當前事務模式為read committed(讀已提交),查詢表user_account的所有記錄:
在客戶端A的事務提交之前,開啟另一個客戶端B,更新表user_account並提交;
在客戶端A查詢表account的所有記錄,與步驟(1)查詢結果一致,沒有出現不可重複讀的問題
可重複讀(repeatable-read)
開啟一個客戶端A,並設定當前事務模式為repeatable read,查詢表user_account的所有記錄
在客戶端A的事務提交之前,開啟另一個客戶端B,更新表user_account並提交
在客戶端A查詢表user_account的所有記錄,與客戶端執行事務之前查詢結果一致,沒有出現不可重複讀的問題
在客戶端B插入一條資料
在客戶端A中還是沒有資料
序列化(serializable)
開啟一個客戶端A,並設定當前事務模式為serializable,查詢表user_account的初始值:
開啟一個客戶端B,並設定當前事務模式為serializable,插入一條記錄報錯,表被鎖了插入失敗,mysql中事務隔離級別為serializable時會鎖表,因此不會出現幻讀的情況,這種隔離級別併發性極低,開發中很少會用到
補充:
1、事務隔離級別為讀提交時,寫資料只會鎖住相應的行
2、事務隔離級別為可重複讀時,如果檢索條件有索引(包括主鍵索引)的時候,預設加鎖方式是next-key 鎖;如果檢索條件沒有索引,更新資料時會鎖住整張表。一個間隙被事務加了鎖,其他事務是不能在這個間隙插入記錄的,這樣可以防止幻讀。
3、事務隔離級別為序列化時,讀寫資料都會鎖住整張表
4、隔離級別越高,越能保證資料的完整性和一致性,但是對併發效能的影響也越大。
5、MYSQL MVCC實現機制參考連結:
https://blog.csdn.net/whoamiyang/article/details/51901888
6、關於next-key 鎖可以參考連結:
https://blog.csdn.net/bigtree_3721/article/details/73731377
參考:https://www.cnblogs.com/huanongying/p/7021555.html