思考一下這個場景:若是重作日誌能夠無限地增大,同時緩衝池也足夠大,那麼是不須要將緩衝池中頁的新版本刷新回磁盤。由於當發生宕機時,徹底能夠經過重作日誌來恢復整個數據庫系統中的數據到宕機發生的時刻。html
可是這須要兩個前提條件:一、緩衝池能夠緩存數據庫中全部的數據;二、重作日誌能夠無限增大mysql
所以Checkpoint(檢查點)技術就誕生了,目的是解決如下幾個問題:一、縮短數據庫的恢復時間;二、緩衝池不夠用時,將髒頁刷新到磁盤;三、重作日誌不可用時,刷新髒頁。算法
當數據庫發生宕機時,數據庫不須要重作全部的日誌,由於Checkpoint以前的頁都已經刷新回磁盤。數據庫只需對Checkpoint後的重作日誌進行恢復,這樣就大大縮短了恢復的時間。sql
當緩衝池不夠用時,根據LRU算法會溢出最近最少使用的頁,若此頁爲髒頁,那麼須要強制執行Checkpoint,將髒頁也就是頁的新版本刷回磁盤。數據庫
當重作日誌出現不可用時,由於當前事務數據庫系統對重作日誌的設計都是循環使用的,並非讓其無限增大的,重作日誌能夠被重用的部分是指這些重作日誌已經再也不須要,當數據庫發生宕機時,數據庫恢復操做不須要這部分的重作日誌,所以這部分就能夠被覆蓋重用。若是重作日誌還須要使用,那麼必須強制Checkpoint,將緩衝池中的頁至少刷新到當前重作日誌的位置。緩存
對於InnoDB存儲引擎而言,是經過LSN(Log Sequence Number)來標記版本的。bash
LSN是8字節的數字,每一個頁有LSN,重作日誌中也有LSN,Checkpoint也有LSN。能夠經過命令SHOW ENGINE INNODB STATUS來觀察:app
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。dom
在InnoDB存儲引擎內部,有兩種Checkpoint,分別爲:Sharp Checkpoint、Fuzzy Checkpoint異步
Sharp Checkpoint 發生在數據庫關閉時將全部的髒頁都刷新回磁盤,這是默認的工做方式,即參數innodb_fast_shutdown=1。可是若數據庫在運行時也使用Sharp Checkpoint,那麼數據庫的可用性就會受到很大的影響。故在InnoDB存儲引擎內部使用Fuzzy Checkpoint進行頁的刷新,即只刷新一部分髒頁,而不是刷新全部的髒頁回磁盤。
Fuzzy Checkpoint:一、Master Thread Checkpoint;二、FLUSH_LRU_LIST Checkpoint;三、Async/Sync Flush Checkpoint;四、Dirty Page too much Checkpoint
一、Master Thread Checkpoint
以每秒或每十秒的速度從緩衝池的髒頁列表中刷新必定比例的頁回磁盤,這個過程是異步的,此時InnoDB存儲引擎能夠進行其餘的操做,用戶查詢線程不會阻塞。
二、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 | +-----------------------+-------+
三、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]
四、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。
在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/