MySQL探秘(八):InnoDB的事務
事務是數據庫最為重要的機制之一,凡是使用過數據庫的人,都了解數據庫的事務機制,也對ACID四個基本特性如數家珍。但是聊起事務或者ACID的底層實現原理,往往言之不詳,不明所以。所以,今天我們就一起來分析和探討InnoDB的事務機制,希望能建立起對事務底層實現原理的具體了解。
事務的四大特性
數據庫事務具有ACID四大特性。ACID是以下4個詞的縮寫:
原子性(atomicity) :事務最小工作單元,要么全成功,要么全失敗 。
一致性(consistency): 事務開始和結束后,數據庫的完整性不會被破壞 。
隔離性(isolation) :不同事務之間互不影響,四種隔離級別為RU(讀未提交)、RC(讀已提交)、RR(可重復讀)、SERIALIZABLE (串行化)。
持久性(durability) :事務提交后,對數據的修改是永久性的,即使系統故障也不會丟失 。
下面,我們就以一個具體實例來介紹數據庫事務的原理,并介紹InnoDB是如何實現ACID四大特性的。
示例介紹
我們首先來看一下具體的示例。大家可以自己親自試驗一下,這樣理解和記憶都會更加深刻。
首先,使用如下的SQL語句創建兩張表,分別是goods和trade,代表貨物和交易。并向goods表中插入一條記錄,id為1的貨物數量為10。
CREATE?TABLE?goods?(id?INT,?num?INT,?PRIMARY?KEY(id));
CREATE?TABLE?trade?(id?INT,?goods_id?INT,?user_id?INT,?PRIMARY?KEY(id));
INSERT?INTO?goods?VALUES(1,?10);
然后打開終端,連接數據庫,開啟會話一,先用BEGIN顯示開啟一個事務。會話一先將goods表中id為1的貨物的數量減一,然后向trade表中添加一筆交易的記錄,最后使用COMMIT顯示提交事務。
而會話二則先查詢goods表中id為1的貨物數量,然后向trade表中添加一筆交易記錄,接著更新goods表中id為1的貨物的數量,最后使用ROLLBACK進行事務的回滾。其中,兩個會話中執行的具體語句和先后順序如下圖所示。
示例具體語句和執行順序
這個示例可以體現數據庫事務的很多特性,我們一一來介紹。首先會話一的操作2更新了id為1的貨物的數量,但是會話二的操作5讀出來的數量仍然是10,這體現了事務的隔離性,使用InnoDB的多版本控制機制實現。
會話二的操作7也要更新同種貨物的數量,此時因為會話一的操作2已經更新了該貨物的數量,InnoDB已經鎖住了該記錄的行鎖,所以操作7會被阻塞,直到會話一COMMIT。但是會話一的操作4和會話二的操作7都是向trade表中插入記錄,后者卻不會因為前者而阻塞,因為二者插入的不是同一行記錄。鎖機制是一種常見的并發控制機制,它和多版本控制機制一起實現了InnoDB事務的隔離性,關于InnoDB鎖相關的具體內容可以參考InnoDB鎖的類型和狀態查詢和InnoDB行鎖算法。
會話一事務最終使用COMMIT提交了事務而會話二事務則使用ROLLBACK回滾了整個事務,這體現了事務的原子性。即事務的一系列操作要么全部執行(COMMIT),要么就全部不執行(ROLLBACK),不存在只執行一部分的情況。InnoDB使用事務日志系統來實現事務的原子性。這里有的同學就會問了,如果中途連接斷開或者Server Crash會怎么樣。能怎么樣,直接自動回滾唄。
一旦會話一使用COMMIT操作提交事務成功后,那么數據一定會被寫入到數據庫中并持久的存儲起來,這體現了事務的持久性。InnoDB使用redo log機制來實現事務的持久性。
而事務的一致性比較難以理解,簡單的講在事務開始時,此時數據庫有一種狀態,這個狀態是所有的Mysql對象處于一致的狀態,例如數據庫完整性約束正確,日志狀態一致等。當事務提交后,這時數據庫又有了一個新的狀態,不同的數據,不同的索引,不同的日志等。但此時,約束,數據,索引,日志等Mysql各種狀態還是要保持一致性。 也就是說數據庫從一個一致性的狀態,變到另一個一致性的狀態。事務執行后,并沒有破壞數據庫的完整性約束。
下面我們就來詳細講解一下上述示例涉及的事務的ACID特性的具體實現原理。總結來說,事務的隔離性由多版本控制機制和鎖實現,而原子性、一致性和持久性通過InnoDB的redo log、undo log和Force Log at Commit機制來實現。
原子性,持久性和一致性
原子性,持久性和一致性主要是通過redo log、undo log和Force Log at Commit機制機制來完成的。redo log用于在崩潰時恢復數據,undo log用于對事務的影響進行撤銷,也可以用于多版本控制。而Force Log at Commit機制保證事務提交后redo log日志都已經持久化。
開啟一個事務后,用戶可以使用COMMIT來提交,也可以用ROLLBACK來回滾。其中COMMIT或者ROLLBACK執行成功之后,數據一定是會被全部保存或者全部回滾到最初狀態的,這也體現了事務的原子性。但是也會有很多的異常情況,比如說事務執行中途連接斷開,或者是執行COMMIT或者ROLLBACK時發生錯誤,Server Crash等,此時數據庫會自動進行回滾或者重啟之后進行恢復。
我們先來看一下redo log的原理,redo log顧名思義,就是重做日志,每次數據庫的SQL操作導致的數據變化它都會記錄一下,具體來說,redo log是物理日志,記錄的是數據庫頁的物理修改操作。如果數據發生了丟失,數據庫可以根據redo log進行數據恢復。
InnoDB通過Force Log at Commit機制實現事務的持久性,即當事務COMMIT時,必須先將該事務的所有日志都寫入到redo log文件進行持久化之后,COMMIT操作才算完成。
當事務的各種SQL操作執行時,即會在緩沖區中修改數據,也會將對應的redo log寫入它所屬的緩存。當事務執行COMMIT時,與該事務相關的redo log緩沖必須都全部刷新到磁盤中之后COMMIT才算執行成功。
數據庫日志和數據落盤機制
redo log寫入磁盤時,必須進行一次操作系統的fsync操作,防止redo log只是寫入了操作系統的磁盤緩存中。參數innodb_flush_log_at_trx_commit可以控制redo log日志刷新到磁盤的策略,它的具體作用可以查閱InnoDB的磁盤文件及落盤機制
redo log全部寫入磁盤后事務就算COMMIT成功了,但是此時事務修改的數據還在內存的緩沖區中,稱其為臟頁,這些數據會依據檢查點(CheckPoint)機制擇時刷新到磁盤中,然后刪除相應的redo log,但是如果在這個過程中數據庫Crash了,那么數據庫重啟時,會依據redo log file將那些還在內存中未更新到磁盤上的數據進行恢復。
數據庫為了提高性能,數據頁在內存修改后并不是每次都會刷到磁盤上。而是引入checkpoint機制,擇時將數據頁落盤,checkpoint記錄之前的數據頁保證一定落盤了,這樣相關的redo log就沒有用了(由于InnoDB redo log file循環使用,這時這部分日志就可以被覆蓋),checkpoint之后的數據頁有可能落盤,也有可能沒有落盤,所以checkpoint之后的redo log file在崩潰恢復的時候還是需要被使用的。InnoDB會依據臟頁的刷新情況,定期推進checkpoint,從而減少數據庫崩潰恢復的時間。檢查點的信息在第一個日志文件的頭部。
數據庫崩潰重啟后需要從redo log中把未落盤的臟頁數據恢復出來,重新寫入磁盤,保證用戶的數據不丟失。當然,在崩潰恢復中還需要回滾沒有提交的事務。由于回滾操作需要undo日志的支持,undo日志的完整性和可靠性需要redo日志來保證,所以崩潰恢復先做redo恢復數據,然后做undo回滾。
在事務執行的過程中,除了記錄redo log,還會記錄一定量的undo log。undo log記錄了數據在每個操作前的狀態,如果事務執行過程中需要回滾,就可以根據undo log進行回滾操作。
數據和回滾日志的邏輯存儲結構.jpg
undo log的存儲不同于redo log,它存放在數據庫內部的一個特殊的段(segment)中,這個段稱為回滾段。回滾段位于共享表空間中。undo段中的以undo page為更小的組織單位。undo page和存儲數據庫數據和索引的頁類似。因為redo log是物理日志,記錄的是數據庫頁的物理修改操作。所以undo log的寫入也會產生redo log,也就是undo log的產生會伴隨著redo log的產生,這是因為undo log也需要持久性的保護。如上圖所示,表空間中有回滾段和葉節點段和非葉節點段,而三者都有對應的頁結構。
我們再來總結一下數據庫事務的整個流程,如下圖所示。
事務的相關流程
事務進行過程中,每次sql語句執行,都會記錄undo log和redo log,然后更新數據形成臟頁,然后redo log按照時間或者空間等條件進行落盤,undo log和臟頁按照checkpoint進行落盤,落盤后相應的redo log就可以刪除了。此時,事務還未COMMIT,如果發生崩潰,則首先檢查checkpoint記錄,使用相應的redo log進行數據和undo log的恢復,然后查看undo log的狀態發現事務尚未提交,然后就使用undo log進行事務回滾。事務執行COMMIT操作時,會將本事務相關的所有redo log都進行落盤,只有所有redo log落盤成功,才算COMMIT成功。然后內存中的數據臟頁繼續按照checkpoint進行落盤。如果此時發生了崩潰,則只使用redo log恢復數據。
隔離性
InnoDB事務的隔離性主要通過多版本控制機制和鎖機制實現,具體可以參考多版本控制,InnoDB鎖的類型和狀態查詢和InnoDB行鎖算法三篇文章。
后記
本來想一篇文章將MySQL的事務機制講明白,寫完自己讀了一遍,還是發現內容有些晦澀難懂,復雜的知識本來就是很難講明白的,夫夷以近,則游者眾;險以遠,則至者少,希望讀者以本文作為一篇指引性的文章,自己再去更加深入的地方去探秘。不過,能將復雜知識講解的通俗簡單也是一項很大的本領,文字和講解能力還是需要提示的。
Mysql探索(一):B-Tree索引
數據庫內部存儲結構探索
MySQL探秘(二):SQL語句執行過程詳解
MySQL探秘(三):InnoDB的內存結構和特性
MySQL探秘(四):InnoDB的磁盤文件及落盤機制
MySQL探秘(五):InnoDB鎖的類型和狀態查詢
MySQL探秘(六):InnoDB一致性非鎖定讀
參考
MySQL · 引擎特性 · InnoDB 事務系統
MySQL · 引擎特性 · InnoDB 崩潰恢復過程
MySQL 數據庫
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。