由於本身對數據的可靠性,可用性方面特別感興趣,因此在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 | 提交事務 |
事務有四個隔離級別
解決了丟失更新
解決了丟失更新+髒讀
解決了丟失更新+髒讀+不可重複讀 (Innodb下也解決了幻讀,原理上文已說明)
解決了丟失更新+髒讀+不可重複讀+幻讀
從上至下,性能越差,安全性越優。
一旦事務提交,則其所作的修改就會永久保存到數據庫中。此時即便系統崩潰,修改的數據也不會丟失。具體實現原理就是在事務commit以前會將,redo log buffer中的數據持久化到硬盤中的redo log file,這樣在commit的時候,硬盤中已經有了咱們修改或新增的數據,由此作到持久化。
簡單總結了一下MySQL事務,對於Redo Undo沒有作到了如指掌的掌握因此介紹篇幅不太大,隨着學習深刻之後會進行相應補充。
-----《MySQL技術內幕 InnoDB存儲引擎》 第2版