MySQL(3)-日誌
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)