前言
InnoDB 有兩塊非常重要的日誌,一個是undo log
,它用來保證事務的原子性
以及InnoDB的MVCC
,另外一個就是是redo log
,它用來保證事務的持久性
。
InnoDB記錄了對數據文件的物理更改
,並保證總是日誌先行
,也就是所謂的WAL,即在持久化數據文件前,保證之前的redo日誌已經寫到磁盤。
LSN
(log sequence number) 用於記錄日誌序號
,它是一個不斷遞增的 unsigned long long 類型整數。在 InnoDB 的日誌系統中,LSN 無處不在,它既用於表示修改髒頁時的日誌序號
,也用於記錄checkpoint,通過LSN,可以具體的定位
到其在redo log文件
中的位置。
爲了管理髒頁,在 Buffer Pool
的每個instance
上都維持了一個flush list
,flush list 上的 page 按照修改這些 page 的LSN號進行排序。因此定期做redo checkpoint點時,選擇的 LSN 總是所有 bp instance 的 flush list 上最老的那個page(擁有最小的LSN)。由於採用WAL的策略,每次事務提交時需要持久化 redo log 才能保證事務不丟。而延遲刷髒頁則起到了合併多次修改的效果,避免頻繁寫數據文件造成的性能問題。
本文的分析基於最新的MySQL 5.7.7-RC版本。
Redo log
InnoDB的redo log可以通過參數innodb_log_files_in_group
配置成多個文件,另外一個參數innodb_log_file_size
表示每個文件的大小。
因此總的redo log大小爲innodb_log_files_in_group * innodb_log_file_size。
Redo log文件以ib_logfile[number]命名
,日誌目錄
可以通過參數innodb_log_group_home_dir
控制。Redo log 以順序
的方式寫入文件
,寫滿時
則回溯到第一個文件
,進行覆蓋寫
。(但在做redo checkpoint時,也會更新第一個日誌文件的頭部checkpoint標記,所以嚴格來講也不算順序寫)。在覆蓋寫之前
,總是要保證對應的髒頁
已經刷到了磁盤
。在非常大的負載
下,Redo log可能產生的速度非常快,導致頻繁的刷髒操作
,進而導致性能下降
,通常在redo checkpoint的日誌超過文件總大小
的76%
之後,InnoDB 認爲這可能是個不安全的點,會強制的preflush髒頁
,導致大量用戶線程停住
。
如果可預期會有這樣的場景:
建議
調大redo log文件
的大小。可以做一次乾淨的shutdown
,然後修改Redo log配置
,重啓
實例
Redo Log 如果要存儲數據
則先存儲數據的日誌
,如果發生宕機導致數據丟失,就通過重做日誌進行數據恢復。重做日誌保證了數據的可靠性
,InnoDB採用了Write Ahead Log(預寫日誌)
策略,即當事務提交時
,先寫重做日誌
,然後再擇時
將髒頁寫入磁盤
。
InnoDB存儲引擎會首先將數據變更
的重做日誌信息先放入重做日誌緩衝中
,然後再按照一定頻率將其刷新到重做日誌文件。重做日誌緩衝一般不需要設置得很大,因爲一般情況每一秒鐘都會講重做日誌緩衝刷新到日誌文件中。可通過配置參數innodb_log_buffer_size
控制,默認爲8MB
。
Redo 寫盤操作
我們所熟悉的參數innodb_flush_log_at_trx_commit
作用於事務提交時,這也是最常見的場景:
- 當設置該值爲
1
時,每次事務提交都要做一次fsync
,這是最安全的配置,即使宕機也不會丟失事務 - 當設置爲
2
時,則在事務提交時只做write操作,只保證寫到系統的page cache
,因此實例crash不會丟失事務,但宕機則可能丟失事務 - 當設置爲
0
時,事務提交不會觸發redo寫操作,而是留給後臺線程每秒一次的刷盤操作
,因此實例crash將最多丟失1秒鐘內的事務
InnoDB的寫入機制
大致入下圖所示。
顯然對性能的影響是隨着持久化程度的增加而增加的。通常我們建議在日常
場景將該值設置爲1
,但在系統高峯期臨時修改成2
以應對大負載。
由於各個事務
可以交叉
的將事務日誌拷貝到log buffer
中,因而一次事務提交
觸發的寫
redo到文件
,可能
隱式的幫別的線程“順便”也寫了redo log
,從而達到group commit
的效果。
Redo checkpoint
check point目的
是爲了定期
將data page buffer
的內容轉儲到data file
。在轉儲時,會記錄check point
發生的”時刻“
。在故障恢復
時候,只需要redo/undo最近的一次checkpoint之後的操作
。
InnoDB的redo log採用覆蓋循環寫
的方式,而不是
擁有無限
的redo空間
;即使擁有理論上極大的redo log空間,爲了
從崩潰中快速恢復
,及時做checkpoint
也是非常有必要的
。
在innodb中,數據刷盤的規則只有一個:checkpoint。innodb存儲引擎中checkpoint分爲兩種:
sharp checkpoint
:在重用redo log文件
(例如切換日誌文件
)的時候,將所有已記錄
到redo log中對應的
髒數據刷到磁盤。fuzzy checkpoint
:一次只刷一小部分
的日誌到磁盤,而非將所有髒日誌刷盤。
有以下幾種情況會觸發該檢查點:master thread checkpoint
:由master線程控制,每秒或每10秒
刷入一定比例
的髒頁到磁盤。flush_lru_list checkpoint
:從MySQL5.6
開始可通過innodb_page_cleaners
變量指定專門負責髒頁刷盤的page cleaner線程的個數
,該線程的目的
是爲了保證lru列表有可用的空閒頁
。async/sync flush checkpoint
:同步刷盤
還是異步刷盤
。例如還有非常多的髒頁
沒刷到磁盤(非常多是多少,有比例控制
),這時候會選擇同步
刷到磁盤,但這很少出現;如果髒頁不是很多
,可以選擇異步
刷到磁盤,如果髒頁很少
,可以暫時不刷
髒頁到磁盤dirty page too much checkpoint
:髒頁太多
時強制觸發
檢查點,目的是爲了保證緩存有足夠的空閒空間
。too much的比例由變量innodb_max_dirty_pages_pct
控制,MySQL 5.6默認
的值爲75
,即當髒頁佔緩衝池的百分之75後,就強制刷一部分髒頁到磁盤。
由於刷髒頁需要一定的時間來完成,所以記錄檢查點的位置
是在每次刷盤結束之後
纔在redo log中標記
的。
InnoDB的master線程
大約每隔10秒
會做一次redo checkpoint
,但不會去preflush髒頁來推進checkpoint點。
通常普通的低壓力負載下,page cleaner
線程的刷髒速度足以保證可作爲檢查點的lsn被及時的推進。但如果系統負載很高時,redo log推進速度過快
,而page cleaner來不及刷髒,這時候就會出現用戶線程陷入同步刷髒並做checkpoint的境地
,這種策略的目的是爲了保證redo log能夠安全的寫入文件而不會覆蓋最近的檢查點。
參考:https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html