一、簡介思考一下這個場景:如果重做日誌可以無限地增大,同時緩衝池也足夠大,那麼是不需要將緩衝池中頁的新版本刷新回磁碟。因為當發生宕機時,完全可以通過重做日誌來恢復整個資料庫系統中的數據到宕機發生的時刻。但是這需要兩個前提條件:1、緩衝池可以緩存資料庫中所有的數據;2、重做日誌可以無限增大因此Chec...
一、簡介
思考一下這個場景:如果重做日誌可以無限地增大,同時緩衝池也足夠大,那麼是不需要將緩衝池中頁的新版本刷新回磁碟。因為當發生宕機時,完全可以通過重做日誌來恢復整個資料庫系統中的數據到宕機發生的時刻。
但是這需要兩個前提條件:1、緩衝池可以緩存資料庫中所有的數據;2、重做日誌可以無限增大
因此Checkpoint(檢查點)技術就誕生了,目的是解決以下幾個問題:1、縮短資料庫的恢復時間;2、緩衝池不夠用時,將臟頁刷新到磁碟;3、重做日誌不可用時,刷新臟頁。
-
當資料庫發生宕機時,資料庫不需要重做所有的日誌,因為Checkpoint之前的頁都已經刷新回磁碟。資料庫只需對Checkpoint後的重做日誌進行恢復,這樣就大大縮短了恢復的時間。
-
當緩衝池不夠用時,根據LRU演算法會溢出最近最少使用的頁,若此頁為臟頁,那麼需要強制執行Checkpoint,將臟頁也就是頁的新版本刷回磁碟。
-
當重做日誌出現不可用時,因為當前事務資料庫系統對重做日誌的設計都是迴圈使用的,並不是讓其無限增大的,重做日誌可以被重用的部分是指這些重做日誌已經不再需要,當資料庫發生宕機時,資料庫恢復操作不需要這部分的重做日誌,因此這部分就可以被覆蓋重用。如果重做日誌還需要使用,那麼必須強制Checkpoint,將緩衝池中的頁至少刷新到當前重做日誌的位置。
對於InnoDB存儲引擎而言,是通過LSN(Log Sequence Number)來標記版本的。
LSN是8位元組的數字,每個頁有LSN,重做日誌中也有LSN,Checkpoint也有LSN。可以通過命令SHOW ENGINE INNODB STATUS來觀察:
mysql> show engine innodb status \G --- LOG --- Log sequence number 34778380870 Log flushed up to 34778380870 Last checkpoint at 34778380870 0 pending log writes, 0 pending chkp writes 54020151 log i/o's done, 0.92 log i/o's/second
Checkpoint發生的時間、條件及臟頁的選擇等都非常複雜。而Checkpoint所做的事情無外乎是將緩衝池中的臟頁刷回到磁碟,不同之處在於每次刷新多少頁到磁碟,每次從哪裡取臟頁,以及什麼時間觸發Checkpoint。
二、Checkpoint分類
在InnoDB存儲引擎內部,有兩種Checkpoint,分別為:Sharp Checkpoint、Fuzzy Checkpoint
Sharp Checkpoint 發生在資料庫關閉時將所有的臟頁都刷新回磁碟,這是預設的工作方式,即參數innodb_fast_shutdown=1。但是若資料庫在運行時也使用Sharp Checkpoint,那麼資料庫的可用性就會受到很大的影響。故在InnoDB存儲引擎內部使用Fuzzy Checkpoint進行頁的刷新,即只刷新一部分臟頁,而不是刷新所有的臟頁回磁碟。
Fuzzy Checkpoint:1、Master Thread Checkpoint;2、FLUSH_LRU_LIST Checkpoint;3、Async/Sync Flush Checkpoint;4、Dirty Page too much Checkpoint
1、Master Thread Checkpoint
以每秒或每十秒的速度從緩衝池的臟頁列表中刷新一定比例的頁回磁碟,這個過程是非同步的,此時InnoDB存儲引擎可以進行其他的操作,用戶查詢線程不會阻塞。
2、FLUSH_LRU_LIST Checkpoint
因為InnoDB存儲引擎需要保證LRU列表中需要有差不多100個空閑頁可供使用。在InnoDB1.1.x版本之前,需要檢查LRU列表中是否有足夠的可用空間操作發生在用戶查詢線程中,顯然這會阻塞用戶的查詢操作。倘若沒有100個可用空閑頁,那麼InnoDB存儲引擎會將LRU列表尾端的頁移除。如果這些頁中有臟頁,那麼需要進行Checkpoint,而這些頁是來自LRU列表的,因此稱為FLUSH_LRU_LIST Checkpoint。
而從MySQL 5.6版本,也就是InnoDB1.2.x版本開始,這個檢查被放在了一個單獨的Page Cleaner線程中進行,並且用戶可以通過參數innodb_lru_scan_depth控制LRU列表中可用頁的數量,該值預設為1024,如:
mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_lru_scan_depth'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | innodb_lru_scan_depth | 1024 | +-----------------------+-------+
3、Async/Sync Flush Checkpoint
指的是重做日誌文件不可用的情況,這時需要強制將一些頁刷新回磁碟,而此時臟頁是從臟頁列表中選取的。若將已經寫入到重做日誌的LSN記為redo_lsn,將已經刷新回磁碟最新頁的LSN記為checkpoint_lsn,則可定義:
checkpoint_age = redo_lsn - checkpoint_lsn
再定義以下的變數:
async_water_mark = 75% * total_redo_log_file_size
sync_water_mark = 90% * total_redo_log_file_size
若每個重做日誌文件的大小為1GB,並且定義了兩個重做日誌文件,則重做日誌文件的總大小為2GB。那麼async_water_mark=1.5GB,sync_water_mark=1.8GB。則:
當checkpoint_age<async_water_mark時,不需要刷新任何臟頁到磁碟;
當async_water_mark<checkpoint_age<sync_water_mark時觸發Async Flush,從Flush列表中刷新足夠的臟頁回磁碟,使得刷新後滿足checkpoint_age<async_water_mark;
checkpoint_age>sync_water_mark這種情況一般很少發生,除非設置的重做日誌文件太小,並且在進行類似LOAD DATA的BULK INSERT操作。此時觸發Sync Flush操作,從Flush列表中刷新足夠的臟頁回磁碟,使得刷新後滿足checkpoint_age<async_water_mark。
可見,Async/Sync Flush Checkpoint是為了保證重做日誌的迴圈使用的可用性。在InnoDB 1.2.x版本之前,Async Flush Checkpoint會阻塞發現問題的用戶查詢線程,而Sync Flush Checkpoint會阻塞所有的用戶查詢線程,並且等待臟頁刷新完成。從InnoDB 1.2.x版本開始——也就是MySQL 5.6版本,這部分的刷新操作同樣放入到了單獨的Page Cleaner Thread中,故不會阻塞用戶查詢線程。
MySQL官方版本並不能查看刷新頁是從Flush列表中還是從LRU列表中進行Checkpoint的,也不知道因為重做日誌而產生的Async/Sync Flush的次數。但是InnoSQL版本提供了方法,可以通過命令SHOW ENGINE INNODB STATUS來觀察,如:
mysql> show engine innodb status \G BUFFER POOL AND MEMORY ---------------------- Total memory allocated 2058485760; in additional pool allocated 0 Dictionary memory allocated 913470 Buffer pool size 122879 Free buffers 79668 Database pages 41957 Old database pages 15468 Modified db pages 0 Pending reads 0 Pending writes: LRU 0, flush list 0, single page 0 Pages made young 15032929, not young 0 0.00 youngs/s, 0.00 non-youngs/s Pages read 15075936, created 366872, written 36656423 0.00 reads/s, 0.00 creates/s, 0.90 writes/s Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000 Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s LRU len: 41957, unzip_LRU len: 0 I/O sum[39]:cur[0], unzip sum[0]:cur[0]
4、Dirty Page too much
即臟頁的數量太多,導致InnoDB存儲引擎強制進行Checkpoint。其目的總的來說還是為了保證緩衝池中有足夠可用的頁。其可由參數innodb_max_dirty_pages_pct控制:
mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_max_dirty_pages_pct' ; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | innodb_max_dirty_pages_pct | 75 | +----------------------------+-------+
innodb_max_dirty_pages_pct值為75表示,當緩衝池中臟頁的數量占據75%時,強制進行Checkpoint,刷新一部分的臟頁到磁碟。在InnoDB 1.0.x版本之前,該參數預設值為90,之後的版本都為75。
三、Checkpoint機制
在Innodb事務日誌中,採用了Fuzzy Checkpoint,Innodb每次取最老的modified page(last checkpoint)對應的LSN,再將此臟頁的LSN作為Checkpoint點記錄到日誌文件,意思就是“此LSN之前的LSN對應的日誌和數據都已經flush到redo log
當mysql crash的時候,Innodb掃描redo log,從last checkpoint開始apply redo log到buffer pool,直到last checkpoint對應的LSN等於Log flushed up to對應的LSN,則恢復完成
那麼具體是怎麼恢復的呢?
如上圖所示,Innodb的一條事務日誌共經歷4個階段:
-
創建階段:事務創建一條日誌;
-
日誌刷盤:日誌寫入到磁碟上的日誌文件;
-
數據刷盤:日誌對應的臟頁數據寫入到磁碟上的數據文件;
-
寫CKP:日誌被當作Checkpoint寫入日誌文件;
對應這4個階段,系統記錄了4個日誌相關的信息,用於其它各種處理使用:
-
Log sequence number(LSN1):當前系統LSN最大值,新的事務日誌LSN將在此基礎上生成(LSN1+新日誌的大小);
-
Log flushed up to(LSN2):當前已經寫入日誌文件的LSN;
-
Oldest modified data log(LSN3):當前最舊的臟頁數據對應的LSN,寫Checkpoint的時候直接將此LSN寫入到日誌文件;
-
Last checkpoint at(LSN4):當前已經寫入Checkpoint的LSN;
對於系統來說,以上4個LSN是遞減的,即: LSN1>=LSN2>=LSN3>=LSN4.
具體的樣例如下(使用show innodb status \G命令查看,Oldest modified data log沒有顯示):
LOG --- Log sequence number 34822137537 Log flushed up to 34822137537 Last checkpoint at 34822133028 0 pending log writes, 0 pending chkp writes 54189288 log i/o's done, 3.00 log i/o's/second
四、日誌保護機制
mysql crash的時候,Innodb有日誌刷盤機制,可以通過innodb_flush_log_at_trx_commit參數進行控制,這裡說的是如何防止日誌覆蓋導致日誌丟失
Innodb的checkpoint和redo log有哪些緊密關係?有幾上名詞需要解釋一下:
-
Ckp age(動態移動): 最老的dirty page還沒有flush到數據文件,即沒有做last checkpoint的範圍
-
Buf age(動態移動): modified page information沒有寫到log中,但已在log buffer
-
Buf async(固定點): 日誌空間大小的7/8,當buf age移動到Buf async點時,強制把沒有寫到log中的modified page information開始寫入到log中,不阻塞事務
-
Buf sync(固定點): 日誌空間大小的15/16,當寫入很大的,buf age移動非常快,一下子到buf sync的點,阻塞事務,強制把modified page information開始寫入到log中。如果不阻塞事務,未做last checkpoint的redo log存在覆蓋危險
-
Ckp async(固定點): 日誌空間大小的31/32,當ckp age到達ckp async,強製做last checkpoint,不阻塞事務
-
Ckp sync(固定點):日誌空間大小,當ckp age到達ckp sync,強製做last checkpoint,阻塞事務,存在redo log覆蓋的危險
接下分析4種情況
-
如果buf age在buf async和buf sync之間
-
如果buf age在buf sync之後(當然這種情況是不存在,mysql有保護機制)
-
如果ckp age在ckp async和ckp sync之間(這種情況是不存在)
-
如果ckp age在ckp sync之後(這種情況是不存在)
第一種情況:
當寫入量巨大時,buf age移動到buf async和buf sync之間,觸發寫出到log中,mysql把儘量多的log寫出,如果寫入量減慢,buf age又移回到“圖一”狀態。如果寫入量大於flush log的速度,buf age最終會和buf sync重疊,這時所有的事務都被阻塞,強制將2*(Buf age-Buf async)的臟頁刷盤,這時IO會比較繁忙。
第二種情況:
當然這種情況是不可能出現,因為如果出現,redo log存在覆蓋的可能,數據就會丟失。buf age會越過log size,buf age的大小可能就超過log size,如果要刷buf age,那麼整個log size都不夠容納所有的buf age。
第三種和第四種情況不存在分析:
ckp age始終位於buf age的後面(左邊),因為ckp age是last checkpoint點,總是追趕buf age(將儘可能多的modified page flush到磁碟),所以buf age肯定是先到達到buf sync。
ckp async及ckp sync存在意義?
mysql中page cache也存在high water及low water,當dirty page觸到low water時,os是開始flush dirty page到磁碟,到high water時,會阻塞一切動作,os會瘋狂的flush dirty page,磁碟會很忙,存在IO Storm,
本文參考:
http://blog.csdn.net/yah99_wolf/article/category/539408
http://www.cnblogs.com/bamboos/p/3532150.html
http://tech.uc.cn/?p=716
http://www.mysqlperformanceblog.com/2011/04/04/innodb-flushing-theory-and-solutions/