關於checkpoint


Ⅰ、Checkpoint

1.1 checkpoint的作用

  • 縮短數據庫的恢復時間
  • 緩沖池不夠用時,將臟頁刷到磁盤
  • 重做日志不可用時,刷新臟頁

1.2 展開分析

page被緩存在bp中,page在bp中和disk中不是時刻保持一致的(page修改一下就刷一次盤是不現實的,是通過checkpoint來玩的)

萬一宕機,重啟的時候disk上那個page需要恢復到原來bp中page的那個版本

那問題是,兩個page版本不一致咋整?沒事,我們做到最終一致就行

那我們就說一下這個最終一致是個怎樣的過程,通過一個例子來說明:

Step1:
一個page讀到bp中時,它的lsn(這個鬼東西待會兒仔細說,先理解為一個flag)是100,然后這個page被modify了,它的lsn變成了130,當對應的事務提交后,修改日志會被記錄到redo里面,此時redo和全局的lsn就相應的變成了130
Step2:
另外一個page之前進bp的時候lsn是50,前面那個page被modify之后,它也被修改,它的lsn變成了140,它這個140的lsn也寫到了redo里,全局lsn變成140
Step3:
關鍵的一步,假設此時lsn為130的page被刷到disk上了(什么時候刷也是個學問,這里不說),而lsn為140的那個page還沒被刷,磁盤上保存的還是老版本,突然宕機了。
Step4:
這時候restart數據庫,就會從磁盤上cp的位置(130)開始讀redo log,一直回放到140,這樣沒被刷到磁盤的那個page就恢復到宕機之前的狀態了。

划重點:

①這個130,140其實就是字節數,也就是說你對這個頁修改產生了10個字節的日志,那么lsn就加10

②page原來讀進bp的lsn甭管,只管它改變了多少字節就行,所以這個lsn的變化肯定是一個單調遞增的過程,其實lsn就是日志寫了多少字節(之前沒理解好,以為各個page的lsn是自己玩自己的)

Ⅱ、LSN(log sequence number)——日志序列號

lsn是用來保存checkpoint的,保存現在刷新到磁盤的位置在哪里

這個130,140其實就是字節數,也就是說你對這個頁修改產生了10個字節的日志,那么lsn就加10,lsn沒有上限,8字節

2.1 lsn存在什么地方?

  • 每個page有一個LSN,page更新一下LSN就會更新一下,記錄在page header中
  • 整個MySQL實例也有一個LSN(這就是checkpoint),記錄在第一個重做日志的前2k的塊里(就給它用,不會被覆蓋)
  • redo log里有一個LSN

全局lsn位置之前的內容已經刷磁盤上,只要恢復它后面的日志,數據就恢復了

2.2 查看lsn和整個checkpoint流程梳理

看page中的lsn,page中其實是保存兩個lsn的,如下:

(root@172.16.0.10) [(none)]>  desc information_schema.INNODB_BUFFER_PAGE_LRU;
+---------------------+---------------------+------+-----+---------+-------+
| Field               | Type                | Null | Key | Default | Extra |
+---------------------+---------------------+------+-----+---------+-------+
| POOL_ID             | bigint(21) unsigned | NO   |     | 0       |       |
| LRU_POSITION        | bigint(21) unsigned | NO   |     | 0       |       |
| SPACE               | bigint(21) unsigned | NO   |     | 0       |       |
| PAGE_NUMBER         | bigint(21) unsigned | NO   |     | 0       |       |
| PAGE_TYPE           | varchar(64)         | YES  |     | NULL    |       |
| FLUSH_TYPE          | bigint(21) unsigned | NO   |     | 0       |       |
| FIX_COUNT           | bigint(21) unsigned | NO   |     | 0       |       |
| IS_HASHED           | varchar(3)          | YES  |     | NULL    |       |
| NEWEST_MODIFICATION | bigint(21) unsigned | NO   |     | 0       |       |
| OLDEST_MODIFICATION | bigint(21) unsigned | NO   |     | 0       |       |
| ACCESS_TIME         | bigint(21) unsigned | NO   |     | 0       |       |
| TABLE_NAME          | varchar(1024)       | YES  |     | NULL    |       |
| INDEX_NAME          | varchar(1024)       | YES  |     | NULL    |       |
| NUMBER_RECORDS      | bigint(21) unsigned | NO   |     | 0       |       |
| DATA_SIZE           | bigint(21) unsigned | NO   |     | 0       |       |
| COMPRESSED_SIZE     | bigint(21) unsigned | NO   |     | 0       |       |
| COMPRESSED          | varchar(3)          | YES  |     | NULL    |       |
| IO_FIX              | varchar(64)         | YES  |     | NULL    |       |
| IS_OLD              | varchar(3)          | YES  |     | NULL    |       |
| FREE_PAGE_CLOCK     | bigint(21) unsigned | NO   |     | 0       |       |
+---------------------+---------------------+------+-----+---------+-------+
20 rows in set (0.00 sec)

newest_modification 頁最新更新完后的lsn
oldest_modification 頁第一次更新完后的lsn
page刷到磁盤的時候,全局的check_point保存的是oldest(只保存第一次修改時的lsn),而page中的lsn保存的是newest

(root@172.16.0.10) [(none)]> show engine innodb status\G
...
---
LOG
---
Log sequence number 15151135824   當前內存中最新的LSN
Log flushed up to   15151135824   redo刷到磁盤的LSN
Pages flushed up to 15151135824   最后一個刷到磁盤上的頁的最新的LSN(NEWEST_MODIFICATION)
Last checkpoint at  15151135815   最后一個刷到磁盤上的頁的第一次被修改時的LSN(OLDEST_MODIFICATION)
...

Log sequence number和Log flushed up這兩個LSN可能會不同,運行過程中后者可能會小於 前者,因為redo日志也是先在內存中更新,再刷到磁盤的

最后一個小於前面三個,為什么?

臟頁會被指向flush list這個就不多贅述了

flush list是根據lsn進行組織的,而且還是用一個page第一次放進來的lsn進行組織的,也就是說這個page再次發生更新,它的位置是不會移動的

分析一波:

bp的LRU列表中,一個page,假設LSN進來的時候是100,當前全局LSN也是100,如果這個page變化了,產生了20字節的日志,這時候page的lsn變成120,並且通過指針指向flush list中去了,但是這個page立馬又被更新產生20字節日志,此時page的lsn為140,而此時在flush list中的lsn還是120(這里意思就是page里面保存了兩種lsn,一個是第一次修改頁的,一個是最后一次修改頁的)

當這個lsn為120的page被刷到disk上,那么disk上的cp就是120了,但是上面的三個值都是140,是不是很好理解呢,那就是說,每個page只更新一次,那這四個值就相等了唄,23333!

為什么這么設計?

為了恢復的時候,保證redo回放的過程的連續性,不會出錯

page A第一次修改后lsn是120,記錄到全局lsn,后面還有個page B被更新,lsn變為140,此時,page A再更新,lsn變為160了。這時候發生宕機,page A被刷到磁盤,page B沒刷過去,如果flush list里面記錄160的話,發生故障重啟時lsn為140的page B怎么恢復?是不是被跳過去了

那從120開始恢復,那個頁已經是160了,為什么還要恢復?

數據庫會檢測,如果page的lsn大於實例的lsn,就不會恢復這個page,跨過去,只將page B從120恢復到140

tips:

①checkpoint不需要實時刷新到磁盤,不是一個頁更新了就要更新磁盤上的cp,磁盤上的cp前置一點是沒有關系的,大不了多scan一點redo log,讀到不回放就是了,而是由master_thread控制,差不多每秒鍾更新一次

②回滾問題

回滾不是通過redo來回滾的,所有的page前滾到一個位置(恢復完),這些page對應的事務還是活躍的,還沒提交,之后這些事務都會通過undo log來undo回滾,但undo是通過redo來恢復的

比如一個頁120-160已經恢復過去了,但是這個事務需要回滾,卻又已經刷到磁盤了,沒關系,通過undo log往回滾一下就好了

事務活躍列表存放在undo段中,只要事務沒提交就在里面,提交后移動到undo的history中,這個歷史列表是用來做purge的,這里面的undo會被慢慢回收

Ⅲ、checkpoint 分類

  • Sharp Checkpoint
    將所有的贓頁都刷新回磁盤,刷新時系統hang住,InnoDB關閉時使用
    相關參數:innodb_fast_shutdown={1|0}
  • Fuzzy Checkpoint
    將部分臟頁刷新回磁盤,對系統影響較小
    innodb_io_capacity來控制,最小限制為100,表示一次最多刷新臟頁的能力,與IOPS相關
    SSD可以設置在4000-8000,SAS最多設置在800多(IOPS在1000左右)

Ⅳ、什么時候刷dirty page

  • 以前在master thread線程中(從flush_list中進行刷新)

    現在都在page_cleaner_thread線程中(每一秒,每十秒)

  • FLUSH_LRU_LIST 刷新

    5.5以前需要保證在LRU_LIST尾部要有100個空閑頁(可替換的頁),即刷新一部分數據 ,保證有100個空閑頁。

    由innodb_lru_scan_depth參數來控制,並不只是刷最后一個頁,默認探測尾部1024個頁(默認),1024個頁中所有臟頁會一起刷掉,該參數是應用到每個Buffer Pool,總數即為該值乘以Buffer Pool的個數,總量超過innodb_io_capacity是不合理的,即此參數不得超過innodb_io_capacity/innodb_buffer_pool_instances,ssd的話,可以適當把這個掃描深度調深一點

  • Async/Sync Flush Checkpoint
    重做日志重用

  • Dirty Page too much
    贓頁比例超過bp總量的一定比例,本來是通過page_cleaner_thread來刷,但是臟頁太多了,就會強行刷,由innodb_max_dirty_pages_pct參數控制

tips:

①頁只會從flush_list中刷新這個觀點是不對的,只有page_cleaner_thread定期問flush_list要臟頁,一個一個刷,刷到innodb_io_capacity的比例值

②LRU list中既存在干凈的頁也存在臟頁,假設最后一個頁,是臟的,另一個線程需要一個頁,free list已經空了,lru會把這個頁淘汰給這個線程去使用,這時候也需要刷新這個臟頁,默認一下探測1024個page,把臟頁刷掉


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM