MySQL事務原理淺析

尋箏發表於2018-05-17

前言

​ 因為自己對資料的可靠性,可用性方面特別感興趣,所以在MySQL事務方面看了很多資料,也看了很多部落格,所以想到自己也寫一篇部落格整理整理自己所學內容,儘量用自己的語言解釋得通俗易懂。

事務經典場景

​ 在很多介紹事務的部落格都會代入這樣一個場景,先簡單說說:

​ A給B轉賬100,A少100,B多100。如果A少100後系統崩潰怎麼辦?B的錢多不了,這樣金錢總數憑空少了100。這裡就需要用到事務了。

什麼是事務?

​ 事務是恢復和併發控制的基本單位,事務有四個特性(ACID),原子性(Atomicity),一致性(Consistency),隔離性(Isolation),永續性(Durability)。本文主要圍繞這四個特性展開介紹。

原子性

​ 原子性就是不可拆分的特性,要麼全部成功然後提交(commit),要麼全部失敗然後回滾(rollback)。若開啟事務,在上述場景就不會出現 A少100 成功,B多100 失敗 這種情況。MySQL通過Redo Log重做日誌實現了原子性,在將執行SQL語句時,會先寫入redo log buffer,再執行SQL語句,若SQL語句執行出錯就會根據redo log buffer中的記錄來執行回滾操作,由此擁有原子性。

一致性

​ 一致性指事務將資料庫從一種狀態轉變為下一種一致的狀態。比如有一個欄位name有唯一索引約束,那麼在事務前後都不能有重複的name出現違反唯一索引約束,否則回滾。在上述場景中即金錢總數總是200,不能憑空增加減少。MySQL通過undo Log實現一致性,執行SQL語句時,會先寫入undo log再寫入 redo log buffer。undo是邏輯日誌,會根據之前的SQL語句進行相應回滾,比如之前是insert那麼回滾時會執行一個delete,一個update會執行 一個相反的update。並且除了回滾,undo log還有一個作用是MVCC,當使用者讀取一行記錄時,若該記錄已經被其他事務佔用,當前事務可通過undo讀取之前的行版本資訊,實現非鎖定讀取。並且undo log也會產生redo log,因為undo log也需要永續性的保護。

隔離性

​ 首先介紹如果沒有隔離性會發生的4種情況

丟失更新

​ A事務撤銷時,把已經提交的B事務的更新資料覆蓋了。這種錯誤可能造成很嚴重的問題,通過下面的賬戶取款轉賬就可以看出來,MySQL通過三級封鎖協議的第一級解決了丟失更新,事務 T 要修改資料 A 時必須加 X 鎖,直到 T 結束才釋放鎖。

時間 取款事務A 轉賬事務B
T1 開始事務 ** **
T2 開始事務
T3 查詢賬戶餘額為1000元
T4 查詢賬戶餘額為1000元
T5 匯入100元把餘額改為1100元
T6 提交事務
T7 取出100元把餘額改為900元
T8 撤銷事務
T9 餘額恢復為1000 元(丟失更新)
髒讀

​ 髒讀主要是讀取到了其他事務的資料,而其他事務隨後發生回滾。MySQL通過三級封鎖協議的第二級解決了髒讀,在一級的基礎上,要求讀取資料 A 時必須加 S 鎖,讀取完馬上釋放 S 鎖。

時間 取款事務A 轉賬事務B
T1 開始事務 ** **
T2 開始事務
T3 查詢賬戶餘額為1000元
T4
T5 匯入100元把餘額改為1100元
T6 查詢賬戶餘額為1100元**(髒讀)**
T7 撤銷事務
T8 匯入100元以為是1200元
不可重複讀

​ 不可重複讀是讀取到資料後,隨後其他事務對資料發生了修改,無法再次讀取。MySQL通過三級封鎖協議的第三級解決了不可重複讀。在二級的基礎上,要求讀取資料 A 時必須加 S 鎖,直到事務結束了才能釋放 S 鎖。

時間 取款事務A 轉賬事務B
T1 開始事務 ** **
T2 開始事務
T3 查詢賬戶餘額為1000元
T4
T5 匯入100元把餘額改為1100元
T6 查詢賬戶餘額為1100元**(不可重複讀)**
T7 提交事務
T8 提交事務
幻讀

​ 幻讀是讀取到資料後,隨後其他事務對資料發生了新增,無法再次讀取。在InnoDB引擎Repeatable Read的隔離級別下,MySQL通過Next-Key Lock以及MVCC解決了幻讀,事務中分為當前讀以及快照讀。

1.快照讀(snapshot read) ------通過MVCC來避免幻讀

簡單的select操作(不包括 select ... lock in share mode, select ... for update)

2.當前讀(current read) ------通過Next-Key Lock 來避免幻讀 Next-Key Lock即間隙鎖(Gap Lock)+行鎖 (Record Lock)

select ... lock in share mode

select ... for update

insert

update

delete

時間 取款事務A 轉賬事務B
T1 開始事務 ** **
T2 開始事務
T3 查詢賬戶餘額為1000元 RMB 100元美元
T4
T5 匯入100歐元
T6 查詢賬戶餘額為1000元 RMB 100元美元 100歐元**(幻讀)**
T7 提交事務
T8 提交事務

​ 事務有四個隔離級別

Read Uncommitted

​ 解決了丟失更新

Read Committed

​ 解決了丟失更新+髒讀

Repeatable Read

​ 解決了丟失更新+髒讀+不可重複讀 (Innodb下也解決了幻讀,原理上文已說明)

Serializable

​ 解決了丟失更新+髒讀+不可重複讀+幻讀

從上至下,效能越差,安全性越優。

永續性

一旦事務提交,則其所做的修改就會永久儲存到資料庫中。此時即使系統崩潰,修改的資料也不會丟失。具體實現原理就是在事務commit之前會將,redo log buffer中的資料持久化到硬碟中的redo log file,這樣在commit的時候,硬碟中已經有了我們修改或新增的資料,由此做到持久化。

總結

​ 簡單總結了一下MySQL事務,對於Redo Undo沒有做到了如指掌的掌握所以介紹篇幅不太大,隨著學習深入以後會進行相應補充。

參考資料

-----《MySQL技術內幕 InnoDB儲存引擎》 第2版

相關文章