innodbbufferpool相關特性

zysql發表於2016-05-20

背景

innodb buffer pool作為innodb最重要的快取,其快取命中率的高低會直接影響資料庫的效能。因此在資料庫發生變更,比如重啟、主備切換例項遷移等等,innodb buffer poll 需要一段時間預熱,期間資料庫的效能會受到明顯影響。
另外mysql 5.7以前innodb buffer pool快取大小修改不是動態的,重啟才能生效。因此innodb buffer pool的預熱和innodb buffer pool大小的動態修改,對效能要求較高的應用來說是不錯的特性,下面我來看看這兩個特性的具體實現。

buffer pool 預熱

mysql 5.6以後支援buffer pool預熱功能。引入了以下引數, 引數具體含義參見官方文件

innodb_buffer_pool_load_now
innodb_buffer_pool_dump_now
innodb_buffer_pool_load_at_startup
innodb_buffer_pool_dump_at_startup
innodb_buffer_pool_filename

buffer pool預熱分為dump過程和load過程,均由後臺執行緒buf_dump_thread完成。
比如使用者發起set命令

set global innodb_buffer_pool_dump_now=on;
set global innodb_buffer_pool_load_now=on;

set 命令會立刻返回,具體操作由buf_dump_thread來實現。

  • dump 過程

鎖buf_pool
遍歷LRU連結串列,將(space, pageno) 先收集到陣列
釋放鎖
再將資料寫入innodb_buffer_pool_filename定有的檔案中

  • load過程

從檔案讀入陣列
按(space,pageno)排序資料
依次同步讀取頁到buffer pool中

dump過程一般比較快,而load過程相對要慢些。

通過Innodb_buffer_pool_dump_statusInnodb_buffer_pool_load_status可檢視dump/load的狀態

另外5.7引入了performance_schema.events_stages_current來顯示load進度,每load 32M會更新一條進度資訊

select * from performance_schema.events_stages_current;
THREAD_ID       19
EVENT_ID        1367
END_EVENT_ID    NULL
EVENT_NAME      stage/innodb/buffer pool load
SOURCE  buf0dump.cc:619
TIMER_START     33393877311000
TIMER_END       33398961258000
TIMER_WAIT      5083947000
WORK_COMPLETED  0
WORK_ESTIMATED  1440
NESTING_EVENT_ID        NULL
NESTING_EVENT_TYPE      NULL

WORK_ESTIMATED表示總page數
WORK_COMPLETED表示當前已load page數

dump檔案的資料格式如下

#cat ib_buffer_pool |more
0,7
0,1
0,3
0,2
0,4
0,11
0,5
0,6

dump檔案比較簡單,我們可以編輯此檔案來預載入指定page,比較靈活。

buffer pool 動態調整大小

5.7 開始支援buffer pool 動態調整大小,每個buffer_pool_instance都由同樣個數的chunk組成(chunks陣列), 每個chunk記憶體大小為innodb_buffer_pool_chunk_size(實際會偏大5%,用於存放chuck中的block資訊)。buffer pool以innodb_buffer_pool_chunk_size為單位進行動態增大和縮小。調整前後innodb_buffer_pool_size應一直保持是innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的倍數。

同樣的buffer pool動態調整大小由後臺執行緒buf_resize_thread,set命令會立即返回。通過InnoDB_buffer_pool_resize_status可以檢視調整的執行狀態。

  • resize流程

    • 如果開啟了AHI,需禁用AHI
    • 如果是收縮記憶體

      1. 計算需收縮的chunk數, 從chunks開始尾部刪除指定個數的chunk.
      2. 鎖buf_pool
      3. 從free_list中摘除待刪chunk的page放入待刪連結串列buf_pool->withdraw
      4. 如果待刪chunk的page為髒頁,則刷髒
      5. 重新載入LRU中要刪除的頁,從LRU中摘除,重新從free列表獲取page老的page放入待刪連結串列buf_pool->withdraw
      6. 釋放buffer pool鎖
      7. 如果需收縮的chunk pages沒有收集全,重複2-6
    • 開始resize

      1. 鎖住所有instance的buffer_pool,page_hash
      2. 收縮pool:以chunk為單位釋放要收縮的記憶體
      3. 清空withdraw列表buf_pool->withdraw
      4. 增大pool:分配新的chunk
      5. 重新分配buf_pool->chunks
      6. 如果改變/縮小超過2倍,會重置page hash,改變桶大小
      7. 釋放buffer_pool,page_hash鎖
      8. 如果改變/縮小超過2倍,會重啟和buffer pool大小相關的記憶體結構,如鎖系統(lock_sys_resize),AHI(btr_search_sys_resize), 資料欄位(dict_resize)等
    • 如果禁用了AHI,此時開啟

由上可以看出,擴大記憶體比縮小記憶體相對容易些。縮小記憶體時,如果遇到有事務一直未提交且佔用了待收縮的page時,導致收縮一直重試,error log會列印這種重試資訊,
包含可能引用此問題的事務資訊。為了避免頻繁重試,每次重試的時間間隔會指數增長。

以上步驟中resize階段buffer pool會不可用,此階段會鎖所有buffer pool, 但此階段都是記憶體操作,時間比較短。收縮記憶體階段耗時可能會很長,也有一定影響,但是每次都是以instance為單位進行鎖定的。
總的來說,buffer pool 動態調整大小對應用的影響並不大。

  • 重新載入LRU中要刪除的頁的影響

search 過程中btr遊標儲存的page可能重新載入過,自適應雜湊儲存的root page也可能重新載入過, 都需要重新讀取。

總結

buffer pool 預熱 和buffer pool 動態調整大小,這兩功能相輔相承的。buffer pool 動態調整大小隻適用於例項在主機本地升級的情況,如果使用者修改buffer pool大小,同時涉及跨機遷移,那麼buffer pool 預熱功能就排上用場了。
另外buffer pool 動態調整儘量在業務低鋒時進行。


相關文章