MySQL底層概述—1.InnoDB記憶體結構

东阳马生架构發表於2024-11-28

大綱

1.InnoDB引擎架構

2.Buffer Pool

3.Page管理機制之Page頁分類

4.Page管理機制之Page頁管理

5.Change Buffer

6.Log Buffer

1.InnoDB引擎架構

(1)InnoDB引擎架構圖

(2)InnoDB記憶體結構

(1)InnoDB引擎架構圖

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

MySQL底層概述—1.InnoDB記憶體結構

(2)InnoDB記憶體結構

MySQL底層概述—1.InnoDB記憶體結構

2.Buffer Pool

(1)Buffer Pool基本概念

(2)如何判斷資料頁是否快取在Buffer Pool

(1)Buffer Pool基本概念

Buffer Pool是緩衝池的意思。Buffer Pool的作用是快取表資料與索引資料,減少磁碟IO,提升效率。

Buffer Pool由快取資料頁(Page)和對快取資料頁進行描述的控制塊組成。控制塊儲存著快取頁的表空間、資料頁號、在Buffer Pool中的地址等。

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

MySQL底層概述—1.InnoDB記憶體結構

注意:Buffer Pool大小為128M指的就是快取頁的總大小。控制塊則一般佔5%,所以每次會多申請6M的記憶體空間用於存放控制塊。

(2)如何判斷資料頁是否快取在Buffer Pool

MySQl中有一個雜湊表資料結構:key是表空間號 + 資料頁號,然後value就是快取頁對應的控制塊。

MySQL底層概述—1.InnoDB記憶體結構

當需要訪問某個頁的資料時:會先從雜湊表中根據表空間號 + 頁號看看是否存在對應的快取頁。如果有,則直接使用。如果沒有,則從Free連結串列中選出一個空閒的快取頁,然後把磁碟中對應的頁載入到該快取頁的位置。

3.Page管理機制之Page頁分類

Buffer Pool的底層採用連結串列資料結構管理Page。在InnoDB訪問表記錄和索引時會在Buffer Pool中的Page頁快取,以後使用同樣的表記錄和索引時,就可以減少磁碟IO操作,提升效率。

Page根據狀態可以分為三種型別:

一.Free Page:空閒Page,未被使用。

二.Clean Page:被使用Page,沒被修改。

三.Dirty Page:被使用Page,已被修改。

髒頁中的資料和磁碟的資料產生了不一致。

MySQL底層概述—1.InnoDB記憶體結構

4.Page管理機制之Page頁管理

(1)Free List空閒緩衝區

(2)Flush List需刷盤的緩衝區

(3)LRU List正在使用的緩衝區

針對上面的三種Page型別,InnoDB會透過三種連結串列結構來維護和管理。

(1)Free List表示空閒緩衝區(管理Free Page)

一.Free連結串列的初始化

Buffer Pool初始化時會先向作業系統申請連續的記憶體空間,然後把它劃分成若干個控制塊&快取頁,接著把所有空閒的快取頁對應的控制塊作為節點放到一個連結串列中,這個連結串列就是Free連結串列。

二.Free連結串列的基節點

Free連結串列中只有一個基節點是不記錄快取頁資訊的(單獨申請空間),基節點存放了Free連結串列的頭節點地址、尾節點地址和節點個數。

MySQL底層概述—1.InnoDB記憶體結構

三.從磁碟載入資料頁的流程

步驟1:首先從Free連結串列中取出一個空閒的控制塊(對應快取頁)。

步驟2:然後把該快取頁對應的控制塊資訊填上,如快取頁所在的表空間、資料頁號之類的資訊。

步驟3:接著把該快取頁對應的Free連結串列節點(即控制塊)從連結串列中移除,表示該快取頁已經被使用了。

(2)Flush List表示需要重新整理到磁碟的緩衝區(管理Dirty Page)

Flush List管理的Dirty Page會按修改時間排序。InnoDB引擎為了提高處理效率,在每次修改快取頁後,並非立刻把修改重新整理到磁碟上,而是在未來某個時間點進行重新整理操作。

凡是被修改過的快取頁對應的控制塊都會作為節點加入到Flush連結串列,Flush連結串列的結構與Free連結串列的結構相似。

MySQL底層概述—1.InnoDB記憶體結構

髒頁既存在於Flush連結串列中,也存在於LRU連結串列中,但它們互不影響。LRU連結串列負責管理Page的可用性和釋放,Flush連結串列負責管理髒頁的刷盤操作。

(3)LRU List表示正在使用的緩衝區(管理Clean Page和Dirty Page)

一.普通LRU演算法

二.普通LRU連結串列的優缺點

三.改進型LRU演算法

四.冷資料區的資料頁何時會被轉到到熱資料區

緩衝區以midpoint為基點:連結串列的前面部分稱為熱資料列表,存放經常訪問的資料,佔63%。連結串列的後面部分稱為冷資料列表,存放使用較少資料,佔37%。

一.普通LRU演算法

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

MySQL底層概述—1.InnoDB記憶體結構

步驟1:當要訪問某個不在Buffer Pool中的資料頁時,就把該資料頁載入到Buffer Pool,並且把其快取頁對應的控制塊作為節點新增到LRU連結串列的頭部。

步驟2:當要訪問某個在Buffer Pool中的資料頁時,就把其快取頁對應的控制塊移動到LRU連結串列頭部。

步驟3:當需要釋放空間時,從最末尾淘汰。

二.普通LRU連結串列的優缺點

優點:(熱資料最快被獲取)

所有最近使用的資料都在連結串列表頭,最近未使用的資料都在連結串列表尾,可以保證熱資料能最快被獲取到。

缺點:(全表掃描 + 預讀機制)

如果發生全表掃描,則可能將真正的熱資料淘汰。由於MySQL中存在預讀機制,很多預讀的頁會被放到LRU連結串列的表頭。如果這些預讀的頁沒用到,那麼就可能會導致真正的熱資料在尾部被淘汰。全表掃描的發生場景是:沒有建立合適的索引或查詢時使用select *等。

MySQL底層概述—1.InnoDB記憶體結構

三.改進型LRU演算法

改進的LRU連結串列分為熱資料和冷資料兩個部分。往LRU連結串列加入元素時並不從表頭插入,而是從中間midpoint位置插入,也就是從磁碟中新讀出的資料會放在冷資料區的頭部。

如果資料很快被訪問,那麼Page就會向熱資料列表頭部移動;如果資料沒有被訪問,那麼Page會逐步向冷資料列表尾部移動,等待淘汰。

MySQL底層概述—1.InnoDB記憶體結構

四.冷資料區的資料頁何時會被轉到到熱資料區

在對某個處於冷資料列表的快取頁進行第一次訪問時,就會在它對應的控制塊中記錄下這個訪問時間。

如果後續的訪問時間與第一次訪問的時間在某個時間間隔內,那麼該快取頁就不會從冷資料列表移動到熱資料列表的頭部,否則就將該快取頁從冷資料列表移動到熱資料列表的頭部,從而避免全表掃描帶來的訪問頻率很低但佔用大量快取頁的問題。

這個間隔時間由innodb_old_blocks_time控制,預設是1s。這也就意味著,對於從磁碟載入到LRU連結串列冷資料列表的快取頁來說,如果第一次和最後一次訪問的時間間隔小於1s,則不會加入熱資料列表。

5.Change Buffer

(1)Change Buffer基本概念

(2)Change Buffer的資料更新流程

(3)為什麼寫緩衝區僅適用於二級索引頁

(4)什麼情況下會進行merge

(5)Change Buffer的使用場景(寫多讀少)

(1)Change Buffer基本概念

Change Buffer是寫緩衝區,用於最佳化對二級索引(輔助索引)頁的更新。

對於DML操作:如果請求的是輔助索引(非唯一鍵索引)且沒有在緩衝池中,那麼不會立刻將資料頁載入到Buffer Pool中,而是會先在Change Buffer中記錄資料的變更,等未來資料被讀取時再將資料合併恢復放到Buffer Pool,以減少磁碟IO。

Change Buffer預設佔Buffer Pool空間25%,最大佔50%。可根據讀寫業務量進行調整,引數是innodb_change_buffer_max_size。

MySQL底層概述—1.InnoDB記憶體結構

(2)Change Buffer的資料更新流程

MySQL底層概述—1.InnoDB記憶體結構

情況1:

對於唯一索引,需要將資料頁讀入記憶體。然後判斷沒有衝突才插入更新值,語句執行結束。

情況2:

對於普通索引,更新一條記錄時,步驟如下:

步驟一:如果該記錄在Buffer Pool中存在,那麼就直接在Buffer Pool中修改,進行一次記憶體操作。

步驟二:如果該記錄在Buffer Pool中不存在(沒有命中),那麼在不影響資料一致性的前提下,InnoDB會將該記錄的更新操作快取在Change Buffer中,先不用去磁碟查詢資料,從而避免一次磁碟IO。

步驟三:當下次查詢該記錄時,InnoDB才會將資料頁讀入記憶體,然後執行Change Buffer中與該記錄有關的操作。

(3)為什麼寫緩衝區僅適用於二級索引頁

如果新增或修改發生在唯一索引中,那麼InnoDB必須要做唯一性校驗。此時就必須查詢磁碟,進行一次IO操作。也就是會直接將記錄查詢到Buffer Pool中,然後在緩衝池修改,不需要在Change Buffer操作了。

如果新增或修改發生在非索引中,那麼InnoDB還是要做唯一性校驗。此時也必須查詢磁碟,進行一次IO操作。

(4)什麼情況下會進行merge

將Change Buffer中資料的變更應用到原資料頁的過程稱為merge。Change Buffer上的快取資料是可以持久化的,以下情況會進行持久化:

一.訪問這個資料頁會觸發merge

二.系統有後臺執行緒會定期merge

三.在資料庫正常關閉的過程中也會執行merge

(5)Change Buffer的使用場景

Change Buffer的主要目的是將記錄的變更操作快取下來,所以在merge發生前應當儘可能多的快取變更資訊,這樣Change Buffer的優勢發揮得就越明顯。

應用場景是寫多讀少的業務。此時頁面在寫完後馬上被訪問的機率較小,Change Buffer使用效果最好。這種業務模型常見的就是賬單類、日誌類的系統。

6.Log Buffer

(1)Log Buffer的作用

(2)Log Buffer的刷盤策略

(3)Adaptive Hash Index

(1)Log Buffer的作用

Log Buffer指的是日誌緩衝區。

Log Buffer是用來儲存要寫入磁碟log檔案(Redo/Undo)的log資料。Log Buffer可以最佳化每次更新操作後要寫檔案而產生的磁碟IO問題,因為每次更新操作都是需要寫log到redo log和undo log磁碟檔案的。

Log Buffer日誌緩衝區的內容會定期重新整理到磁碟log檔案中,Log Buffer日誌緩衝區滿時會自動將其重新整理到磁碟。當遇到BLOB或多行更新的大事務時,增加日誌緩衝區可節省磁碟IO。

Log Buffer主要是用於記錄InnoDB引擎日誌,InnoDB在DML操作時會產生redo和undo日誌。

MySQL底層概述—1.InnoDB記憶體結構

Log Buffer空間滿了,會自動寫入磁碟,預設16M。可以透過將innodb_log_buffer_size引數調大,減少磁碟IO頻率。

(2)Log Buffer的刷盤策略

innodb_flush_log_at_trx_commit引數控制日誌重新整理行為,預設為1。

一.innodb_flush_log_at_trx_commit = 0

每隔1秒寫日誌檔案Log Buffer和刷盤操作,最多丟失1秒資料。寫日誌檔案Log Buffer -> OS cache -> 刷盤OS Cache -> 磁碟檔案。

二.innodb_flush_log_at_trx_commit = 1

事務提交,立刻寫日誌檔案和刷盤,資料不丟失,但是會頻繁IO操作。

三.innodb_flush_log_at_trx_commit = 2

事務提交,立刻寫日誌檔案Log Buffer,每隔1秒鐘進行刷盤操作。

(3)Adaptive Hash Index

自適應雜湊索引,用於最佳化對Buffer Pool資料的查詢,InnoDB儲存引擎會監控對錶索引的查詢。

自適應雜湊索引指的是:如果觀察到建立雜湊索引可以帶來速度的提升,則建立雜湊索引。InnoDB儲存引擎會自動根據訪問的頻率和模式來為某些頁建立雜湊索引。

相關文章