MySQL事務原理淺析

前言

​ 由於本身對數據的可靠性,可用性方面特別感興趣,因此在MySQL事務方面看了不少資料,也看了不少博客,因此想到本身也寫一篇博客整理整理本身所學內容,儘可能用本身的語言解釋得通俗易懂。
<!-- more -->數據庫

事務經典場景

​ 在不少介紹事務的博客都會代入這樣一個場景,先簡單說說:安全

​ 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 鎖。ci

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

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

時間 取款事務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版

相關文章
相關標籤/搜索