詳解MySQL事務原理
老劉是即將找工作的研究生,自學大資料開發,一路走來,感慨頗深,網上大資料的資料良莠不齊,於是想寫一份詳細的大資料開發指南。這份指南把大資料的【基礎知識】【框架分析】【原始碼理解】都用自己的話描述出來,讓夥伴自學從此不求人。
您的點贊是我持續更新的動力,禁止白嫖,看了就要有收穫,一起加油。
今天給大家分享的是大資料開發基礎部分MySQL的事務,事務在MySQL知識點中非常重要的部分,很多夥伴只是知道MySQL的四大特性,但不知道其中的原理,老劉這次給大家詳細的描述MySQL四大特性的原理,MySQL事務篇的大綱如下:
什麼是事務?
在MySQL中的事務是由儲存引擎實現的,而且支援事務的儲存引擎不多,我們主要講解InnoDB儲存引擎中的事務。
事務處理可以用來維護資料庫的完整性,保證成批的 SQL 語句要麼全部執行,要麼全部不執行。
事務用來管理 DDL、DML、DCL 操作,比如 insert,update,delete 語句,預設是自動提交的。
事務的四大特性(ACID)
Atomicity(原子性):構成事務的的所有操作必須是一個邏輯單元,要麼全部成功,要麼全部失敗。 Consistency(一致性):資料庫在事務執行前後狀態都必須是穩定的或者是一致的,就是說事務開始和結束後,資料庫的完整性不會被破壞。 Isolation(隔離性):事務之間不會相互影響。由鎖機制和MVCC機制來實現的,其中MVCC(多版本併發控制):優化讀寫效能(讀不加鎖、讀寫不衝突),四種隔離級別為RU(讀未提交)、RC(讀已提交)、RR(可重複讀)、SERIALIZABLE (序列化)。 Durability(永續性):事務執行成功後必須全部寫入磁碟,事務提交後,對資料的修改是永久性的,即使系統故障也不會丟失。
事務的使用
begin或start transaction:開啟一個事務;
commit:提交一個事務,並使已對資料庫進行的所有修改稱為永久性的;
rollback:回滾會結束使用者的事務,並撤銷正在進行的所有未提交的修改。
ACID實現原理
下面我們就來詳細講解一下上述示例涉及的事務的ACID特性的具體實現原理。總結來說,事務的隔離性由多版本控制機制和鎖實現,而原子性、一致性和永續性通過InnoDB的redo log、undo log和ForceLog at Commit機制來實現。
重做日誌Redo Log
如果要儲存資料則先儲存資料的日誌,一旦記憶體崩了,則可以從日誌找重做日誌保證了資料的可靠性,InnoDB採用了Write Ahead Log(預寫日誌)策略,即當事務提交時,先寫重做日誌,然後再擇時將髒頁寫入磁碟。如果發生宕機導致資料丟失,就通過重做日誌進行資料恢復。
回滾日誌Undo Log
資料庫崩潰重啟後需要從redo log中把未落盤的髒頁資料恢復出來,重新寫入磁碟,保證使用者的資料不丟失。當然,在崩潰恢復中還需要回滾沒有提交的事務。由於回滾操作需要undo日誌的支援,undo日誌的完整性和可靠性需要redo日誌來保證,所以崩潰恢復先做redo恢復資料,然後做undo回滾。
所以,在事務執行的過程中,除了記錄redo log,還會記錄一定量的undo log。undo log記錄了資料在每個操作前的狀態,如果事務執行過程中需要回滾,就可以根據undo log進行回滾操作。
Force Log at Commit機制
它實現事務的永續性,即當事務提交時,必須先將該事務的所有日誌寫入到重做日誌檔案進行持久化,然後事務的提交操作完成才算完成。為了確保每次日誌都寫入到重做日誌檔案,在每次將重做日誌緩衝寫入重做日誌後,必須呼叫一次fsync操作(作業系統),將緩衝檔案從檔案系統快取中真正寫入磁碟。
總結一下就是redo log用於在崩潰時恢復資料,undo log用於對事務的影響進行撤銷,也可以用於多版本控制。而Force Log at Commit機制保證事務提交後redo log日誌都已經持久化。
原子性
原子性是指一個事務是一個不可分割的工作單位,其中的操作要麼都做,要麼都不做。例如銀行轉賬要麼成功,要麼失敗,是不存在中間的狀態!
Undo Log是實現原子性的關鍵,靠的就是undo log。當事務對資料庫進行修改時,InnoDB會生成對應的undo log。undo log它屬於邏輯日誌,它記錄的是sql執行相關的資訊。當發生回滾時,InnoDB會根據undo log的內容做與之前相反的工作:對於每個insert,回滾時會執行delete;對於每個delete,回滾時會執行insert;對於每個update,回滾時會執行一個相反的update,把資料改回去。
以update操作為例:當事務執行 update 時,其生成的 undo log 中會包含被修改行的主鍵(以便知道修改了哪些行)、修改了哪些列、這些列在修改前後的值等資訊,回滾時便可以使用這些資訊將資料還原到 update 之前的狀態。
永續性
永續性是指事務執行成功後必須全部寫入磁碟,事務提交後,對資料的修改是永久性的,即使系統故障也不會丟失。
InnoDB作為MySQL的儲存引擎,資料是存放在磁碟中的,但如果每次讀寫資料都需要磁碟IO,效率會很低。為此,InnoDB提供了快取(Buffer Pool),Buffer Pool中包含了磁碟中部分資料頁的對映,作為訪問資料庫的緩衝:當從資料庫讀取資料時,會首先從Buffer Pool中讀取,如果Buffer Pool中沒有,則從磁碟讀取後放入Buffer Pool;當向資料庫寫入資料時,會首先寫入Buffer Pool,Buffer Pool中修改的資料會定期重新整理到磁碟中。
雖然Buffer Pool的使用大大提高了讀寫資料的效率,但是也有別的問題,當MySQL宕機,而此時Buffer Pool中修改的資料還沒有重新整理到磁碟,就會導致資料的丟失,事務的永續性無法保證。
於是,優秀的程式設計師們引入了redo log,當我們對資料進行修改時,除了修改Buffer Pool中的資料,還會在redo log中記錄這次操作。當事務提交時,會呼叫fsync介面對redo log進行刷盤。如果MySQL宕機,重啟時可以讀取redo log中的資料,對資料庫進行恢復。
還有一點必須知道就是redo log採用的是WAL策略,所有修改先寫入日誌,再更新到Buffer Pool,保證了資料不會因MySQL宕機而丟失,從而滿足了永續性要求。
隔離性
在MySQL隔離性中,一般有兩種情況:
要求同一時刻只能有一個事務對資料進行寫操作,InnoDB通過鎖機制來保證這一點。 在進行讀操作的時候,可能出現髒讀、不可重複讀、幻讀的問題。
首先講第一種情況,MySQL要求同一時刻只能有一個事務對資料進行寫操作,InnoDB通過鎖機制來保證這一點。
鎖機制的基本原理可以理解為:事務在修改資料之前,需要先獲得相應的鎖;獲得鎖之後,事務便可以修改資料;該事務操作期間,這部分資料是鎖定的,其他事務如果需要修改資料,需要等待當前事務提交或回滾後釋放鎖。
至於鎖機制中的鎖,一般就是之前講到的MySQL鎖,大家可以去看看這篇MySQL鎖的內容。
接著講第二種情況,讀操作可能出現髒讀、不可重複讀、幻讀的問題。
隔離性追求的是併發情形下事務之間互不干擾,但是在事務的併發操作中可能會出現一些問題:
丟失更新:兩個事務針對同一資料都發生修改操作時,會存在丟失更新問題。 髒讀:對於兩個事務 T1,T2,T1 讀取了已經被 T2 更新但還沒有被提交的欄位。之後,若 T2 回滾,T1讀取的內容就是臨時且無效的。 不可重複讀:對於兩個事務T1,T2,T1 讀取了一個欄位,然後 T2 更新了該欄位。之後,T1再次讀取同一個欄位,發現欄位的內容不一樣。要求,多次讀取資料的時候,在一個事務中讀出的都應該是一樣的。一般是由於 update 操作引發,所以將來執行的時候要特別注意。 幻讀:對於兩個事務T1,T2,T1 從一個表中讀取了一個欄位,然後 T2 在該表中插入了一些新的行。之後。如果 T1 再次讀取同一個表,就會多出幾行。就是發現數據的數量不一樣。要求,在一個事務中多次去讀取資料的時候都應該是一樣的。
雖然有上述這些問題,但MySQL資料庫為我們提供的四種隔離級別(由低到高):
Read uncommitted (讀未提交):最低級別,任何情況都無法保證。 Read committed (RC,讀已提交):可避免髒讀的發生。 Repeatable read (RR,可重複讀):可避免髒讀、不可重複讀的發生。(InnoDB預設級別為RR,它可以解決幻讀,主要原因是Next-Key(Gap)鎖,只有RR才能使用Next-Key鎖) Serializable (序列化):可避免髒讀、不可重複讀、幻讀的發生。
解決髒讀、不可重複讀、幻讀的問題使用的是MVCC,即多版本的併發控制協議。它說的就是在同一時刻,不同的事務讀取到的資料可能是不同的(即多版本)。
MVCC最大的優點是讀不加鎖,因此讀寫不衝突,併發效能好。InnoDB實現MVCC,多個版本的資料可以共存,主要是依靠資料的隱藏列( 也可以稱之為標記位 )和undo log。其中資料的隱藏列包括了該行資料的版本號、刪除時間、指向undo log的指標等等;當讀取資料時,MySQL可以通過隱藏列判斷是否需要回滾並找到回滾需要的undo log,從而實現MVCC。
MVCC如何解決髒讀、不可重複讀、幻讀的問題
1、MVCC解決髒讀
當事務T1在第三個時刻讀取自己的餘額時,會發現資料已被T2事務修改,並且T2的狀態還沒有提交。此時事務A讀取最新資料後,根據資料的undo log執行回滾操作,得到事務T2修改前的資料,從而避免了髒讀。
2、MVCC解決不可重複讀
當事務T1在第二個時刻第一次讀取資料時,會記錄該資料的版本號(資料的版本號是以row為單位記錄的),假設版本號為1;當事務T2對自己的餘額進行修改並且提交時,該行記錄的版本號增加,假設版本號為2;當事務T1在第五個時刻再一次讀取資料時,發現數據的版本號2大於第一次讀取時記錄的版本號1,因此會根據undo log執行回滾操作,得到版本號為1時的資料,從而實現了可重複讀。
3、MVCC解決幻讀
InnoDB實現的RR通過next-key lock機制避免了幻讀現象。
next-key lock是行鎖的一種,實現相當於record lock(記錄鎖) + gap lock(間隙鎖),它的特點是不僅會鎖住記錄本身(record lock的功能),還會鎖定一個範圍(gap lock的功能)。
當事務T1在第二個時刻第一次讀取0<id<5資料時,會進行標記,標記內容包括資料的版本號等,並且標記的不只是id=1的資料,還將範圍(0,5)進行了標記。我們接著在第三個時刻插入新的使用者並且提交事務,最後第五個時刻再次讀取0<id<5資料時,便可以發現id=2的資料比之前標記的版本號更高,此時再結合undo log執行回滾操作,避免了幻讀。
稍微總結下,InnoDB通過鎖機制、資料的隱藏列、undo log和類next-key lock,實現了一定程度的隔離性,可以滿足大多數場景的需要。不過需要說明的是,RR雖然避免了幻讀問題,但是畢竟不是Serializable,不能保證完全的隔離。
一致性
一致性是事物追求的最終目標,前面提到的原子性,隔離性,永續性都是為了保證資料庫的一致性。也就是說ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔離性)、D(永續性)是手段,是為了保證一致性,資料庫提供的手段。資料庫必須要實現AID三大特性,才有可能實現一致性。
總結
本文作為大資料開發指南MySQL的第四篇詳細介紹了MySQL事務的內容,尤其是MySQL四大特性的原理。希望大家能夠跟著老劉的文章,好好捋捋思路,爭取能夠用自己的話把這些知識點講述出來!
儘管當前水平可能不及各位大佬,但老劉會努力變得更加優秀,讓各位小夥伴自學從此不求人!
大資料開發指南地址如下:
github:https://github.com/BigDataLaoLiu/BigDataGuide 碼雲:https://gitee.com/BigDataLiu/BigDataGuide 如果有相關問題,聯絡公眾號:努力的老劉。文章都看到這了,點贊關注支援一波!