3. InnoDB日誌
3.1 InnoDB架構
分為
- 記憶體區域架構
- 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的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)