【mysql】Innodb存儲引擎是如何保證事務的ACID四個原則的

事務是數據庫區別於文件系統的重要特性之一。
事務會把數據庫從一種一致狀態轉換爲另一種一致狀態。

事務的四個特性

  • 原子性:原子性是指整個數據庫事務是不可分割的工作單位。只有使事務中所有的數據庫操作都執行成功,纔算整個事務成功。事務中一個SQL語句執行失敗,已經執行成功的SQL語句必須撤銷,數據庫狀態應該退回到執行事務前的狀態。
  • 一致性:一致性是指事務將數據庫從一種狀態轉變爲下一種一致狀態。在事務之前和事務結束之後,數據庫的完整性約束沒有被破壞。
  • 隔離性:又可叫做併發控制、可串行化、鎖等。事務的隔離性要求每個讀寫事務的對象對其他事務的操作對象能相互隔離,即該事務提交前對其他事務都不可見,通常使用鎖來實現。(下篇文章總結)
  • 持久性:事務一旦提交,其結果就是永久性的。即使發生宕機等故障,數據庫也能將數據恢復。

INNODB如何保證事務的四個特性?

由於隔離性關係到鎖的相關知識內容比較多,下篇文章總結,先來總結其他的三個特性的實現。

原子性、一致性、持久性通過數據庫的redo log和undo log來完成。redo log稱爲重做日誌,用來保證事務的原子性和持久性。undo log用來保證事務的原子性和一致性。
在這裏插入圖片描述

redo log

redo log即重做日誌。通常是物理日誌(物理日誌是冪等的),記錄的是頁的物理修改操作。由兩部分組成一是內存中的日誌緩衝,二是重做日誌文件。在事務進行時,Innodb首先將重做日誌信息先放入重做日誌緩衝中,然後按照一定頻率將其刷新到重做日誌文件中。
redolog整個結構和執行過程
重做日誌的寫入時間:
InnoDB是事務的存儲引擎,其通過Force Log at Commit 機制實現事務的持久性,即當事務提交COMMIT時,必須將該事務的所有日誌寫入到重做日誌文件中進行持久化。重做日誌是在事務進行中不斷地寫入,即並不是隨事務提交的順序進行寫入的。

重做日誌的寫入方式:
在INNODB存儲引擎中,重做日誌都是以512字節進行存儲的。這意味着重做日誌緩衝,重做日誌文件都是以塊的方式進行保存的。稱之爲重做日誌塊,每塊的大小爲512字節。
若一個頁中產生的重做日誌數量大於512字節,那麼需要分割爲多個重做日誌塊進行存儲。此外,由於重做日誌塊的大小和磁盤扇區大小一樣,都是512字節,因此重做日誌的寫入可以保證原子性,不需要doublewrite技術。

重做日誌格式:
| redo_log_type | space | page_no | redo log body |
由於innodb存儲引擎的存儲管理是基於頁的,故其重做日誌格式也是基於頁的。

LSN 檢查點技術:
LSN是Log Sequence Number的縮寫,代表的是日誌序列號。LSN代表的是事務寫入重做日誌的總量。例如當前重做日誌LSN爲1000,有一個事務T1寫入了100字節的重做日誌,那麼LSN就變成了1100,若又有事務T2寫入了200字節的重做日誌,那麼LSN就變成了1300.

LSN不僅記錄在重做日誌中,還存在每個頁中。在每個頁的頭部,有一個值FIL_PAGE_LSN,記錄了該頁的LSN。在頁中,LSN表示該頁最後刷新時LSN的大小。因爲重做日誌記錄的是每個頁的日誌,因此頁中的LSN用來判斷頁是否需要進行恢復操作。
例如:頁P1的LSN爲10000,而數據庫啓動時,INNODB檢測到寫入重做日誌中的LSN爲13000,並且該事務已經提交,那麼數據庫需要進行恢復操作,將重做日誌應用到P1頁中,同樣的,對於重做日誌中LSN小於P1頁中的LSN不需要進行重做,因爲P1頁中的LSN表示頁已經被刷新到該位置。

在mysql中通過SHOW ENGINE INNODB STATUS可以查看LSN的情況:
log sequence number :當前的lsn
log flushed up to :刷新到重做日誌文件中的lsn
last checkpoint at 表示刷新到磁盤的lsn,checkpoint的位置,在恢復過程中,通常僅需要恢復checkpoint位置之後的數據。

重做日誌是如何保證持久性的?
爲了確保每次日誌都寫入重做日誌文件,在每次將重做日誌緩衝寫入重做日誌文件後,innodb存儲引擎都需要調用一次fsync操作,通過fsync來確保日誌總能被持久化到磁盤中。同時,重做日誌文件的持久化操作是在數據庫數據持久化操作之前已經完成的。

undo log

重做日誌記錄了事務的行爲,可以很好地通過其對頁進行“重做”操作。但是事務有時還需要進行回滾操作,這時就需要undo。

undo 用於將數據庫邏輯地恢復到數據庫事務執行語句之前的樣子。

在對數據庫進行修改時,innodb存儲引擎不但會產生redo,還會產生一定量的undo。這樣如果用戶只需事務或語句由於某種原因失敗了,或者rollback語句請求回滾,就可以利用undo將數據回滾到修改之前的樣子。

undo log作用

  • 回滾
  • MVCC 多版本併發控制實現數據庫一致性非鎖定讀

undo log 存儲
存放在數據庫內部的一個特殊段(segment)中,這個段稱爲undo段。undo段位於共享表空間內。undo是邏輯日誌,因此只是將數據庫邏輯的恢復到原來的樣子。

事務在undo log segment分配頁並寫入undo log的這個過程同樣需要寫入重做日誌。當事務提交時,innodb存儲引擎會做以下兩件事情:

  • 將undo log放入列表中,以供之後的purge操作。
  • 判斷undo log所在的頁是否可以重用,如果可以分配給下個事務使用

事務提交後並不能馬上刪除undo log 以及undo log所在的頁。這是因爲可能還有其他事務需要通過undo log來得到行記錄之前的版本。故事務提交時將undo log放入一個鏈表中,是否可以刪除undo log及undo log所在的頁由purge線程來判斷

undo log 分類

  • insert undo log: insert undo log 是指在insert操作中產生的undo log。因爲insert 操作紀律,只對事務本身可見,對其他事務不可見。故該undo log可以在事務提交後直接刪除。不需要進行purge操作。
  • update undo log:update undo log 記錄的是對delete和update操作產生的undo log。該undo log可能需要提供MVCC機制,因此不能再事務提交時就進行刪除。提交時放入undo log鏈表,等待purge線程進行最後的刪除。

對於delete操作並不是直接刪記錄,而是將記錄標記爲已刪除,也就是將記錄的delete flag設置爲1,而記錄最終的刪除是在purge操作中完成。
對於update操作,如果更新對象的非主鍵值,則記錄一條TRX_UNDO_UPD_EXIST_REC的undo日誌。對於更新對象的主鍵值,則會將原有記錄標記爲刪除,再插入一條新的記錄,會有一個類型爲TRX_UNDO_DEL_MARK_REC和一條類型爲TRX_UNDO_INSERT_MARK_REC兩條undo日誌。

undo log如何保證事務的一致性?
由於在事務執行時,不僅會產生redo log,也同時會產生undo log,當事務執行時發生宕機或者異常時,通過回滾執行undo log能使數據返回初始狀態來保證數據一致性。

innodb存儲引擎如何保證事務的原子性?
原子性分爲兩種情況,一種是所有語句執行完成,或者一句失敗,之前執行過的語句全部回滾。
第一種情況由redo log完成。當事務commit後,數據庫持久化未完成時發生宕機,可以通過執行redo log完成事務的提交。
第二種情況由undo log完成。當一條語句執行失敗時,事務回滾,執行undo log使得數據恢復到原來的樣子。

以上關於innodb存儲引擎保證事務的原子性,一致性和持久性的實現,還有很多待完善的地方,下片文章總結關於隔離性相關的知識。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章