天天看點

MySQL - InnoDB 記憶體結構解析

作者:閃念基因

關鍵詞彙

Buffer Pool,控制塊,緩存頁,資料頁,Free 連結清單,Flush 連結清單,LRU 連結清單,Page, Change Buffer, Log Buffer
  1. Buffer Pool
  2. Change Buffer
  3. Log Buffer
  4. Adaptive Hash Index

下面是官方的 InnoDB 引擎架構圖,主要分為記憶體結構和磁盤結構兩大部分。

MySQL - InnoDB 記憶體結構解析

1、Buffer Pool 概述

Buffer Pool:緩沖池,簡稱 BP。其作用是用來緩存表資料與索引資料,減少磁盤 IO 操作,提升效率。

Buffer Pool 由緩存資料(Page)和對緩存資料頁進行描述的控制塊組成,控制塊中存儲着對應緩存頁的所屬的表空間、資料頁的編号、以及對應緩存頁在 Buffer Pool 中的位址等資訊。

Buffer Pool 預設大小是 128M, 以 Page 頁為機關,Page 頁預設大小 16K,而控制塊的大小約為資料頁的5%,大概是800位元組。

MySQL - InnoDB 記憶體結構解析
注:Buffer Pool 大小為 128M 指的就是緩存頁的大小,控制塊則一般占5%,是以每次會多申請 6M 的記憶體空間用于存放控制塊。

如何判斷一個頁是否在 BufferPool 中緩存 ?

MySQl 中有一個哈希表資料結構,它使用表空間号+資料頁号,作為一個 key,然後緩沖頁對應的控制塊作為 value。

MySQL - InnoDB 記憶體結構解析

當需要通路某個頁的資料時,先從哈希表中根據表空間号和頁号看看是否存在對應的緩沖頁。如果有,則直接使用;如果沒有,就從 free 連結清單中選出一個空閑的緩沖頁,然後把磁盤中對應的頁加載到該緩沖頁的位置。

2、Page 頁分類

Page 根據狀态可以分為三種類型:

MySQL - InnoDB 記憶體結構解析

1、Free Page:空閑 Page,未使用

2、Clean Page:被使用 Page 但是資料沒有修改過

3、Dirty Page:髒頁,使用過資料被修改過,與磁盤資料産生不一緻

針對上面所說的三種 page 類型,InnoDB 通過三種連結清單結構來維護和管理。

BP 的底層采用連結清單資料結構管理 Page。在 InnoDB 通路表記錄和索引時會在 Page頁 中緩存,以後使用可以減少磁盤 IO 操作,提升效率。

2.1、Page 頁管理之 Free 連結清單

free list:表示空閑緩沖區,管理 free page

Buffer Pool 的初始化過程中,是先向作業系統申請連續的記憶體空間,然後把它劃分成若幹個控制塊&緩沖頁的鍵值對。

free 連結清單是把所有空閑的緩沖頁對應的控制塊作為一個個的節點放到一個連結清單中,這個連結清單便稱之為 free 連結清單

基節點:free 連結清單中隻有一個基節點是不記錄緩存頁資訊(單獨申請空間),它裡面就存放了 free 連結清單的頭節點的位址,尾節點的位址,還有 free 連結清單裡目前有多少個節點。

MySQL - InnoDB 記憶體結構解析

磁盤加載頁的流程:

  1. 從 free 連結清單中取出一個空閑的控制塊,對應緩沖頁。
  2. 把該緩沖頁對應的控制塊的資訊填上,例如:頁所在的表空間、頁号之類的資訊。
  3. 把該緩沖頁對應的 free 連結清單節點即控制塊從連結清單中移除。表示該緩沖頁已經被使用了。

2.2、Page 頁管理之 Flush 連結清單

flush list: 表示需要重新整理到磁盤的緩沖區,管理 dirty page,内部 page 按修改時間排序。

InnoDB 引擎為了提高處理效率,在每次修改緩沖頁後,并不是立刻把修改重新整理到磁盤上,而是在未來的某個時間點進行重新整理操作。是以需要使用到 flush 連結清單存儲髒頁,凡是被修改過的緩沖頁對應的控制塊都會作為節點加入到 flush 連結清單。

flush 連結清單的結構與 free 連結清單的結構相似

MySQL - InnoDB 記憶體結構解析

髒頁即存在于 flush 連結清單,也在 LRU 連結清單中,但是兩種互不影響,LRU 連結清單負責管理 page 的可用性和釋放,而 flush 連結清單負責管理髒頁的刷盤操作。

2.3、Page 頁管理之普通 LRU 連結清單

LRU = Least Recently Used(最近最少使用):就是末尾淘汰法,新資料從連結清單頭部加入,釋放空間時從末尾淘汰。

MySQL - InnoDB 記憶體結構解析
  1. 當要通路某個頁時,如果不在 Buffer Pool,需要把該頁加載到緩沖池,并且把該緩沖頁對應的控制塊作為節點添加到 LRU 連結清單的頭部。
  2. 當要通路某個頁時,如果在 Buffer Pool中,則直接把該頁對應的控制塊移動到 LRU 連結清單的頭部。
  3. 當需要釋放空間時,從最末尾淘汰。

2.4、普通 LRU 連結清單的優缺點

優點: 所有最近使用的資料都在連結清單表頭,最近未使用的資料都在連結清單表尾,保證熱資料能最快被擷取到。

缺點:如果發生全表掃描(比如:沒有建立合适的索引 or 查詢時使用 select * 等),則有很大可能将真正的熱資料淘汰掉。由于MySQL中存在預讀機制,很多預讀的頁都會被放到 LRU 連結清單的表頭。如果這些預讀的頁都沒有用到的話,會導緻很多尾部的緩沖頁很快就會被淘汰。

MySQL - InnoDB 記憶體結構解析

2.5、Page 頁管理之改進型 LRU 連結清單

改進型 LRU:連結清單分為 new 和 old 兩個部分,加入元素時并不是從表頭插入,而是從中間 midpoint 位置插入(就是說從磁盤中新讀出的資料會放在冷資料區的頭部),如果資料很快被通路,那麼 page 就會向 new 清單頭部移動,如果資料沒有被通路,會逐漸向old尾部移動,等待淘汰。

MySQL - InnoDB 記憶體結構解析

冷資料區的資料頁什麼時候會被轉到到熱資料區呢 ?

  1. 如果該資料頁在 LRU 連結清單中存在時間超過1s,就将其移動到連結清單頭部(連結清單指的是整個LRU 連結清單)。
  2. 如果該資料頁在 LRU 連結清單中存在的時間短于1s,其位置不變(由于全表掃描有一個特點,就是它對某個頁的頻繁通路總耗時會很短)。
  3. 1s這個時間是由參數 innodb_old_blocks_time 控制的。

3、Change Buffer

3.1、概述

Change Buffer:寫緩沖區,是針對二級索引(輔助索引) 頁的更新優化措施

作用::在進行 DML 操作時,如果請求的是輔助索引(非唯一鍵索引)沒有在緩沖池中時,并不會立刻将磁盤頁加載到緩沖池,而是在 CB 記錄緩沖變更,等未來資料被讀取時,再将資料合并恢複到 BP 中。

ChangeBuffer 占用 BufferPool 空間,預設占25%,最大允許占50%,可以根據讀寫業務量來進行調整。參數 innodb_change_buffer_max_size ;

MySQL - InnoDB 記憶體結構解析

3.2、Change Buffer 資料更新流程

場景1: 對于唯一索引來說,需要将資料頁讀如記憶體,判斷沒有沖突,插入這個值,語句執行結束;

場景2: 對于普通索引來說,則是将更新記錄在 change buffer 流程如下

MySQL - InnoDB 記憶體結構解析

1、更新一條記錄時,該記錄在 bp 存在,直接在 bp 修改,一次記憶體操作。

2、如果該記錄在 bp 不存在(沒有命中),在不影響資料一緻性的前提下,InnoDB 會将這些更新操作緩存在 change buffer 中不用再去磁盤查詢資料,避免一次磁盤 IO。

3、當下次查詢記錄時,會将資料頁讀入記憶體,然後執行 change buffer 中與這個頁有關的操作。通過這種方式就能保證這個資料邏輯的正确性。

問題一:為什麼寫緩沖區,僅适用于非唯一普通索引頁?

如果在索引設定唯一性,在進行修改時,InnoDB 必須要做唯一性校驗,是以必須查詢磁盤,做一 次 IO 操作。會直接将記錄查詢 Buffer Pool 中,然後在緩沖池修改,不會在 Change Buffer 操作。

問題二:什麼情況進行 merge?

将 change buffer 中的操作應用到原資料頁,得到最新結果的過程稱為 merge。

change buffer,實際上它是可以持久化的資料。也就是說,change buffer 在記憶體中有拷貝,也會被寫入到磁盤上,以下情況會進行持久化:

  1. 通路這個資料頁會觸發 merge。
  2. 系統有背景線程會定期 merge。
  3. 在資料庫正常關閉( shutdown )的過程中,也會執行 merge 操作。

3.3、change buffer 使用場景

change buffer 的主要目的就是将記錄的變更動作緩存下來,是以在 merge 發生之前應當盡可能多的緩存變更資訊,這樣 change buffer的優勢發揮的就越明顯。

應用場景:對于寫多讀少的業務來說,頁面在寫完以後馬上被通路到的機率比較小,此時 change buffer 的使用效果最好。這種業務模型常見的就是賬單類、日志類的系統。

4、Log Buffer

Log Buffer:日志緩沖區,用來儲存要寫入磁盤上的 Log 檔案(redo/undo)的資料,日志緩沖區内容定期重新整理道磁盤 Log 檔案中,日志緩沖區滿時會自動将其重新整理到磁盤,節省磁盤 io

Log Buffer 主要記錄 innoDB 引擎日志,在 DML 操作時會産生 Redo 和 Undo 日志

MySQL - InnoDB 記憶體結構解析

Log Buffer 空間滿了,會自動寫入磁盤。可以通過将将 innodb_log_buffer_size 參數調大,減少磁盤 IO 頻率

innodb_flush_log_at_trx_commit 參數控制日志重新整理行為,預設為1

0 : 每隔1秒寫日志檔案和刷盤操作(寫日志檔案 LogBuffer-->OS cache,刷盤 OS cache-->磁盤檔案),最多丢失1秒資料

1:事務送出,立刻寫日志檔案和刷盤,資料不丢失,但是會頻繁 IO 操作

2:事務送出,立刻寫日志檔案,每隔1秒鐘進行刷盤操作

SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';

MySQL - InnoDB 記憶體結構解析

SHOW VARIABLES LIKE 'innodb_log_buffer_size';

MySQL - InnoDB 記憶體結構解析

5、Adaptive Hash Index

自适應哈希索引,用于優化 bp 資料查詢

6、總結

本期講了關于 InnoDB 記憶體結構,後續有機會在繼續介紹 InnoDB 磁盤結構。

參考文章

https://dev.mysql.com/doc/refman/5.7/en/innodb-in-memory-structures.html

https://dev.mysql.com/doc/refman/5.7/en/innodb-architecture.html

作者:來電

來源:微信公衆号:政采雲技術

出處:https://mp.weixin.qq.com/s/U_KVZNGlTUvT3_ElZDhN7g