mysql 事務日誌

劣技砖猿發表於2024-05-02

事務日誌簡介

事務有四種特性:原子性、一致性、隔離性、永續性,詳情請看《mysql 事務的基礎知識》。其中隔離性由鎖機制實現,原子性、一致性由 undo 日誌(undo log 稱為回滾日誌,回滾記錄到某個特定版本)來保證,永續性則是由 redo 日誌(redo log 稱為重做日誌,提供寫操作,恢復提交事務修改的頁操作)來保證。
image

有的人認為 undo 是 redo 的逆過程,其實不然。redo 和 undo 都是儲存引擎層(InnoDB)生成的日誌,都可以視為一種恢復操作,但 redo log 記錄的是物理級別上的頁修改操作,undo log 記錄的是邏輯操作日誌,主要用於事務的回滾(undo log 記錄的是每個修改操作的逆操作)和一致性非鎖定讀物(undo log 回滾行記錄到某種特定的版本——MVCC,即多版本併發控制)。

redo 日誌

InnoDB 儲存引擎是以頁為單位來管理儲存空間的。在真正訪問頁之前,需要把磁碟上的頁快取到記憶體中的 Buffer Pool 之後才可以訪問。所有的更改都必須先更新緩衝池中的資料,然後緩衝池中的髒頁會以一定的頻率被刷入磁碟(checkPoint機制),透過緩衝池來最佳化 CPU 和磁碟之間的鴻溝,這樣就可以保證整體效能不會下降太快。
InnoDB 引擎的事務正是採用了 WAL 技術(Write-Ahead Logging),這種技術的思想就是先寫日誌,在寫磁碟,只有日誌寫入成功,才算事務提交成功,這裡的日誌就是 redo log。當發生當機且資料未刷到磁碟的時候,可以透過 redo log 來恢復,保證一致性。

redo 日誌的特點和好處

使用 redo 日誌的好處:降低了刷盤頻率、redo 日誌佔用空間非常小。
特點:

  • redo 日誌是順序寫入磁碟的:在執行事務的過程中,每執行一條語句,就可能產生若干條 redo 日誌,這些日誌是按照產生的順序寫入磁碟的,也就是順序 IO,效率比隨機 IO 快。
  • 事務執行過程中,redo log 不斷記錄:redo log 跟 bin log 的區別是 redo log 是儲存引擎產生的,而 bin log 是資料庫層產生的。

redo 的組成

redo log 可以簡單分為兩個部分:

  • 重做日誌緩衝(redo log buffer):儲存在記憶體中,是易失的,預設 16M,最大 4096M,最小 1M。在伺服器啟動時就像作業系統申請了一大片連續的記憶體空間,稱為 redo 日誌緩衝區。這篇記憶體空間被劃分成若干個連續的 redo log block,一個 redo log block 佔用 512 位元組。
  • 重做日誌檔案(redo log file):儲存在硬碟中,是持久的。預設有兩個檔案(由innodb_log_files_in_group的值決定) ib_logfile0 和 ib_logfile1。

redo 的整體流程

image

  1. 先將原始資料從磁碟中讀到記憶體中,修改資料的記憶體複製。
  2. 生成一條重做日誌並寫入 redo log buffer。記錄的是資料被修改後的值
  3. 當事務 commit 時,將 redo log buffer 中的內容重新整理到 redo log file。對 redo log file 採用追加的方式
  4. 定期將記憶體中修改的資料重新整理到磁碟中

redo log 的刷盤策略

redo log buffer 刷盤到 redo log file 的過程中並不是真正的刷到磁碟中去,只是刷入到檔案系統快取(page cache)中去(這是現代作業系統為了提高檔案寫入效率做的一個最佳化),真正的寫入會交給作業系統自己決定。那麼對於 InnoDB 來說就存在一個問題,如果交給系統來同步,如果系統當機,那麼資料也就丟失了。
針對這種情況,InnoDB 給出 innodb_flush_log_at_trx_commit引數,該引數控制 commit 提交事務時,如果將 redo log buffer 中的日誌重新整理到 redo log file 中。它支援三種策略:

  • 設定為0:表時每次事務提交時不進行輸盤操作(系統預設 master thread 每隔 1 秒進行一次重做日誌的同步)
  • 設定為1:該值為預設值,表示每次事務提交時都將進行同步,主動刷盤操作
  • 設定為2:表示每次事務提交時都只把 redo log buffer 內容寫入 page cache,不進行同步。由 OS 自己決定什麼時候同步到磁碟檔案。

undo 日誌

undo log 是事務原子性的保證。在事務中更新資料的前置操作其實是要先寫入一個 undo log。
事務需要保證原子性,也就是事務中的操作要麼全部完成,要麼什麼也不做。因此,undo log 的一個作用就是用來回滾資料,另一個作用是 MVCC,即在 InnoDB 儲存引擎中 MVCC 的實現是透過 undo 日誌來 完成。當使用者讀取一行記錄時,若該記錄已經被其他事務佔用,當前事務就可以透過 undo 讀取之前的版本資訊,一次實現非鎖定讀取。

undo 日誌的儲存結構

InnoDB 對 undo log 的管理採用段的方式,也就是回滾段(rollback segment),儲存於共享表空間 ibdata 中。每個回滾段記錄了 1024 個 undo log segment,而每個 undo log segment 中進行 undo 頁的申請。

  • 在 InnoDB1.1 版本之前(不包括 1.1 版本),只有 rollback segment,因此支援同時在寫的事務限制為 1024。
  • 從 1.1 版本開始 InnoDB 最大支援 128 個 rollback segment,故其支援同時在寫的事務限制提高到了 128 * 1024 個。

從 InnoDB 1.2 開始,可透過引數對 rollback segment 做進一步的設定。這些引數包括:

  • innodb_undo_directory:設定 rollback segment 檔案所在的路徑。這意味著 rollback segment 可以存放在共享表空間外的位置,即可以設定為獨立表空間。預設值為“./”,表示當前 InnoDB 儲存引擎的目錄。
  • innodb_undo_logs:設定 rollback segment 的個數,預設值為 128.在 InnoDB 1.2 中,該引數用來替換之前版本的引數 innodb_rollback_segments。
  • innodb_undo_tablespaces:innodb_undo_tablespaces:設定成 rollback segment 檔案的數量,這樣 rollback segment 可以較為平均的分佈在多個檔案中。設定該引數後,會在路徑 innodb_undo_directory 看到 undo 為字首的檔案,該檔案就代表 rollback segment 檔案。

undo 日誌的型別

在 InnoDB 儲存引擎中,undo log 分為:

  • insert undo log:指在 insert 操作中產生的 undo log。因為 insert 操作的記錄只對事務本身可見,對其他事務不可見(事務隔離性的要求),故 undo log 可以在事務提交後直接刪除。不需要進行 purge 操作。
  • update undo log:記錄的是 delete 和 update 操作產生的 undo log。該 undo log 可能需要提供 MVCC 機制,因此不能在事務提交時就刪除。提交時放入 undo log 連結串列,等待 purge 執行緒進行最後的刪除。

回滾段中的資料分類:

  • 未提交的回滾資料(uncommitted undo information):該資料所關聯的事務並未提交,用於實現讀一致性,所以該資料不能被其他事務的資料覆蓋。
  • 已提交但未過期的回滾資料(committed undo information):改資料關聯的事務已經提交,但仍受到 undo retention 引數的保持時間的影響。
  • 事務已經提交併過期的資料(expired undo information):事務已經提交,而且資料儲存時間已經超過 undo retention 引數指定的時間,屬於已經過期的資料。當回滾段滿了之後,會優先覆蓋“事務已經提交併過期的資料”。

相關文章