MySQL(3)-日誌

3. InnoDB日誌

3.1 InnoDB架構

InnoDB architecture diagram showing in-memory and on-disk structures.

分為

  • 記憶體區域架構
    • buffer pool
    • log buffer
  • 磁碟區域架構
    • redo log
    • undo log

2.1.1 記憶體區域架構

1)Buffer Pool
  • 定義

    InnoDB對會將磁碟中經常訪問的數據所在的頁存入Buffer Pool中以加快訪問速度,這種操作稱為預讀,後續對磁碟上某條數據做修改時,也是先讀取到buffer pool中,再修改buffer pool中的數據

  • 組成

    由多個Page組成,其中存儲了磁碟上的多行數據,方便了大容量的高效讀操作,此外,將Page組織成鏈表結構,便於採用LRU進行記憶體淘汰

2)Change Buffer
  • 定義

    對於非唯一索引,如果在修改其對應的數據時,行記錄不存在於Buffer Pool,那麼就將修改操作記錄到Change Buffer,等待後面真正讀取到該記錄到Buffer Pool中時,再將結果進行merge,並修改磁碟中的數據

  • 為何必須非唯一

    對於唯一索引,InnoDB需要對記錄的唯一性做校驗,也就必須從磁碟中讀取到數據,而change buffer的存在意義是盡量避免不必要的隨機磁碟io,而對於唯一性校驗來說,磁碟io是不可避免的,且由於唯一索引b+樹的特點,在是自增的情況下,插入操作是一次順序io,效率是很高的,也就不必有change buffer

3)自適應哈希索引
  • 定義

    為了讓MySQL性能更接近於基於記憶體的資料庫,對於經常訪問的數據,會根據數據的特點,以表的某幾列建立hash索引,存儲結構類似於hashmap,採用拉鏈法解決hash衝突,使得查詢複雜度降低到O(1)

  • 特性

      • 適應等值比較
      • 適應單條數據查詢
      • 不適應範圍查詢
      • 不可用範圍或like比較
      • 不適用連表查詢
4)Log Buffer

是磁碟上log文件的緩衝區,修改會先記錄到此buffer,之後非同步的同步到磁碟上的log文件

3.1.2 磁碟區域架構

除了表、索引、表空間之外還有

1)Doublewrite Buffer

buffer pool中對頁面的修改資訊不會直接同步到對應的表中,而是會以大的連續塊的形式調用fsync()寫入到雙寫快取中,這樣在os、存儲引擎或其他異常發生時,可以從雙寫快取中找到備份

2)redo log
3)undo log

3.2 bin log&redo log&undo log

3.2.1 binlog

1)組成
  • binlog cache

    作為binlog的快取,會先寫入cache中

  • binlog-xxx

    存在於磁碟上的binlog文件,是append only式創建

2)存儲內容

可以配置成三種類型

  • statement

    記錄為邏輯日誌,存儲提交的事務的DML語句和事務號

  • row

    記錄為物理日誌,記錄了實際的修改,會使得日誌比較大

  • mixed

    前兩者的結合

3)作用

多用於主從同步和冷備

4)層級

位於MySQL server層

3.2.2 redo log

1)組成
  • redo log buffer

    作為redo log的快取,會先寫入cache中

  • ib_logfile-xxx

    存在於磁碟上的redo log文件,是循環寫入的,對於已經提交的事務,會清空

2)存儲內容

是物理日誌和邏輯日誌的結合,物理體現在記錄了具體某一頁上發生了修改,邏輯體現在頁內的實際修改是以記錄DML語句完成的

3)作用

用於宕機恢復,保證一致性

4)層級

位於存儲引擎層

3.2.3 undo log

1)組成
  • undo log

    • update log

      對於未提交的事務內發生的update操作,會存儲相反的update

    • insert log

      對於未提交的事務內發生的delete操作,會存儲對應的insert

    • delete log

      對於未提交的事務內發生的insert操作,會存儲對應的delete

上述log會以事務號的順序編排成一個鏈表以便於確定要回滾到哪個事務

2)存儲內容

是邏輯日誌,存儲相反的DML語句

3)作用

用於回滾,保證原子性

4)層級

位於存儲引擎層

3.3 事務內修改流程

假設事務記憶體在一條insert語句,那麼實際執行流程如下

  • 導入buffer並修改
  • 記錄undo log
  • 記錄redo log buffer並寫盤
  • 2PC提交

詳細流程如下

3.3.1 導入buffer並修改

檢查buffer pool中是否存在要更新的數據所在的頁,如果不存在,需要將頁面讀入buffer pool,之後修改對應的數據

3.3.2 記錄undo log

將delete語句記錄到磁碟中的undo log,組織成鏈表

3.3.3 記錄redo log buffer並寫盤

將修改記錄到buffer,之後根據寫盤策略,將buffer中的數據寫入到redo log,同步的策略有下面三種,通過設定innodb_flush_log_at_trx_commit完成

  • 0

    每次提交都寫入redo log buffer,之後每秒執行fsync()同步到redo log

  • 1

    每次提交都直接寫入到redo log中

  • 2

    每次提交寫入os cache,之後根據innodb_flush_log_at_timeout配置,決定多久後fsync()

3.3.4 2PC提交

1)流程

由於InnoDB的redo log出現晚於binlog,且兩者都用於crash safe,那麼就需要保證binlog和redolog中數據的一致性,這裡採用類似於分散式事務中的想法,採用兩階段提交的方式來保證一致性,流程如下(此時默認redo log寫盤已經執行)

  • 進入Prepare階段,設置redo log為prepare

  • 寫入binlog cache

  • 進入Commit階段,設置redo log為commit

  • 根據binlog的寫盤策略,將binlog cahce寫入binlog,策略有下面三種

    • 0

      每次提交寫入到os cache

    • 1

      每次提交都直接寫入bin log

    • N

      每次都寫入os cache,累計N個事務再fsync()

2)異常分析
  • 寫redo log宕機

    這時可以根據已經落盤的undo log進行回滾

  • 寫binlog cache宕機

    這時一致性未達成,根據undo log做回滾

  • 提交後宕機

    檢查redo log中存儲的最新事務號是否存在於binlog,如果不存在,將不存在的回滾

3.4 預寫日誌

預寫日誌(Write Ahead Log)即在修改磁碟內的數據頁中的資訊前,將修改資訊先寫入磁碟中的log文件,如redo log和bin log

這麼做有以下優勢

  • 順序io

    由於redo log和binlog落盤時是順序寫入的,而如果直接修改磁碟中數據頁中的數據,是隨機io,效率非常低

  • 並發量大

    讀寫者互不阻塞

  • fsync調用次數少

    相較於直接寫入磁碟,WAL的fsync調用次數很少,無需每個事務都寫盤

3.5 sync、fsync和fdatasync

3.5.0 延遲寫

linux中為了減少磁碟io,在寫入磁碟時會經歷如下步驟

  • 寫入os cache
  • 寫入output queue
  • 寫入磁碟

只有當os cache滿時,才會複製到output queue;只有output queue隊首的數據會被寫入到磁碟

3.5.1 sync

將數據同步到os cache,並不會等待到寫入磁碟後返回,這需要update守護進程周期性調用sync將os cache輸出到output queue保證寫盤成功

3.5.2 fsync

對於某個文件的fd,調用fsync會在寫盤成功後返回,寫入的數據包括inode中的文件屬性以及文件的數據部分

3.5.3 fdatasync

同樣對於某個文件的fd,調用fdatasync會在寫盤成功後返回,寫入的數據只有文件的數據部分,不包括inode

3.6 double write和redo log

3.6.1 為何需要Doublewrite

buffer pool中的數據要寫入到磁碟時,是以頁為單位,如果寫入過程出現宕機,那麼就算有redo log也無法恢復,由於redo log每個頁內記錄的是邏輯日誌,而邏輯日誌需要保證表中的數據是完備且未改動的,這樣where條件才不會失效,因而redo log並不能保證頁面級別的crash safe

3.6.2 Doublewrite實現

1)組成

分為兩部分

  • 記憶體中的double write buffer
  • 物理磁碟上共享表空間中連續的128個頁,即2個區(extent),大小同樣為2MB
2)機制

流程如下

  • 每次臟頁會先複製到double write buffer
  • 分兩次,每次1MB將頁面資訊書順序寫入到磁碟上的共享表空間
  • 將buffer中的數據調用fsync離散寫入磁碟

這樣由於在磁碟的共享表空間中記錄了頁面的詳細修改資訊,就可以在同步頁面到磁碟上時保證crash safe

# 參考

MySQL :: MySQL 5.7 Reference Manual :: 14.4 InnoDB Architecture

重要,知識點:InnoDB的插入緩衝 (qq.com)

為什麼數據不會丟,InnoDB的Double Write,你必須知道 – 掘金 (juejin.cn)

MySQL–buffer pool、redo log、undo log、binlog_黃智霖的部落格-CSDN部落格

Write-Ahead Logging (sqlite.org)

redo log的被動刷盤機制 – 雲+社區 – 騰訊雲 (tencent.com)

Linux IO同步函數:sync、fsync、fdatasync | Byte_Liu’s Blog (byteliu.com)

Tags: