你真的了解Innodb存儲引擎?


前言

前幾篇記錄了如何查看SQL執行計划、數據庫事務相關的知識點

除了這兩個,數據庫還有兩個是非常重要的,必須要考的

就是存儲引擎和索引

今天先記錄以下InnoDB存儲引擎相關的知識點

MySQL存儲引擎

在MySQL存儲引擎中,最為廣知的存儲引擎是InnoDB和MyISAM存儲引擎

而這兩個存儲引擎的區別應該大家都清楚:

 

而MySQL目前默認的存儲引擎就是InnoDB

「如何查看表使用的是哪種存儲引擎?」

show table status like 'table_name'\G 

 

InnoDB

是MySQL的默認事務性存儲引擎,最重要、使用最廣泛。

用來處理大量的短期事務。

InnoDB的性能和自動崩潰恢復特性,使得它在非事務性存儲的需求中也有廣泛的應用。

采用MVVC來支持高並發,並實現了四個標准的隔離級別,默認隔離級別是RR(Repeatable Read可重復讀),並通過間隙鎖來防止幻讀的出現。

InnoDB表是基於聚簇索引建立的,聚簇索引對主鍵查詢由很高的性能,不過它的二級索引(非主鍵索引)必須包含主鍵列。

「具體架構圖如下」

 

上半部分是實例層(計算層),位於內存中

下半部分是物理層,位於文件系統中

實例層

分為線程和內存,Innodb重要的線程有Master Thread,此線程是Innodb的主線程,負責調度其他各線程。

Master Thread 的優先級最高, 其內部包含幾個循環:

主循環(loop)

后台循環(background loop)

刷新循環(flush loop)

暫停循環(suspend loop)。

Master Thread 會根據其內部運行的相關狀態在各循環間進行切換

大部分操作在主循環(loop)中完成,其包括1s和10s兩種操作:

「1s操作:」

日志緩沖刷新到磁盤

最多刷新100個新臟頁到磁盤

執行並改變緩沖的操作

若當前沒有用戶活動,可能切換到后台循環等


「10s操作:」

最多刷新100個新臟頁到磁盤

合並至多5個被改變的緩沖

日志緩沖刷新到磁盤

刪除無用的Undo頁

刷新 100 個或者 10 個臟頁到磁盤(總是)產生一個檢查點(總是)等。


「buf dump thread:」

將緩存池中內容dump到磁盤中,實現MySQL熱啟動

「page_cleaner_thread:」

將緩存池的臟頁刷新到磁盤

「purge thread:」

將不再使用的Undo頁回收

「read_thread:」

處理讀請求,並負責將數據頁從磁盤中讀取出來

「write_thread:」

負責將數據頁從緩沖區寫入磁盤,page_cleaner 線程發起刷臟頁操作后就開始工作了。

「redo_log_thread:」

負責將日志緩沖區中的內容刷新到Redo log文件中

「insert_buffer_thread:」

負責把 Insert Buffer 中的內容刷新到磁盤


緩存結構

 

緩沖池中緩存的數據頁類型有:

  • 索引頁
  • 數據頁
  • undo頁
  • 插入緩沖(insert buffer)
  • 自適應哈希索引(adaptive hash index)
  • InnoDB存儲的鎖信息(lock info)
  • 數據字典信息(data dictionary)

 

Redo log 日志緩存

InnoDB存儲引擎會首先將重做日志信息先放入重做日志緩沖中,然后再按照一定頻率將其刷新到重做日志文件


「緩沖頁管理算法:」

頁:磁盤管理的最小單位,默認16K。類型有:數據頁,undo頁,系統頁

一般緩沖的管理算法就是LRU(Least recently used)。

一般是把剛如緩沖頁的放在LRU的頭部,作為最近訪問的元素,最后的一個元素被淘汰。

(1)頁已經在緩沖池里,那就只做“移至”LRU頭部的動作,而沒有頁被淘汰;

(2)頁不在緩沖池里,除了做“放入”LRU頭部的動作,還要做“淘汰”LRU尾部頁的動作;

比如:有一個LRU鏈

 

「第一種情況:」

查找元素為0的,那么鏈表就變成下面這樣子

 

「第二種情況:」

查找元素為10的,但10不在鏈中,所以將10添加到head,而tail節點6去除

 

MySQL在此基礎上進行了改造,原因是:

(1)預讀失效:

「預讀:」

磁盤讀寫,並不是按需讀取,而是按頁讀取,一次至少讀一頁數據(一般是16K),如果未來要讀取的數據就在頁中,就能夠省去后續的磁盤IO,提高效率。

「預讀失效:」

預讀(Read-Ahead)提前把頁放入了緩沖池,但最終MySQL並沒有從頁中讀取數據,稱為預讀失效。

「如何優化?」

(1)讓預讀失效的頁,停留在緩沖池LRU里的時間盡可能短;

(2)讓真正被讀取的頁,才挪到緩沖池LRU的頭部;


所以MySQL將LRU分成兩部分,分別是:

  • 新生代
  • 老生代

新老生代首位相連。

新頁加入緩沖池時,只加入老生代的頭部,

「如果數據真正被讀取(預讀成功),才會加入到新生代的頭部」

「如果數據沒有被讀取,則會比新生代里的“熱數據頁”更早被淘汰出緩沖池」

新生代跟老生代的比例是 5 : 3

例如:綠色的是新生代,黃色是老生代

 

如果插入一個9的頁,就是如下圖:

老年代最后一個被淘汰

 

如果此時9被讀取,那么就變成如下:

9稱為新生代head節點,而原先新生代的tail節點5就變成老年代的head節點

 

(2)緩沖池污染

當一個SQL查詢要掃描大量數據,導致把緩沖池中所有頁全部替換,導致大量熱數據被換出去,這就是緩沖池污染

MySQL在老生代中添加了停留時間窗口

如果數據被讀取了並且在老身代中停留時間超過這個窗口,那么才會被加入新生代頭部

「如何查看這個停留時間:」

參數innodb_old_blocks_time

 


「緩沖池相關參數:」

 

物理層

物理層在邏輯上分為

  • 系統表空間
  • 用戶表空間
  • Redo日志。

系統表空間里有 ibdata 文件和一些 Undo,ibdata 文件里有 insert buffer 段、double write段、回滾段、索引段、數據字典段和 Undo 信息段。

用戶表空間是指以 .ibd 為后綴的文件,文件中包含 insert buffer 的 bitmap 頁、葉子頁(這里存儲真正的用戶數據)、非葉子頁。InnoDB 表是索引組織表,采用 B+ 樹組織存儲,數據都存儲在葉子節點中,分支節點(即非葉子頁)存儲索引分支查找的數據值。

Redo 日志中包括多個 Redo 文件,這些文件循環使用,當達到一定存儲閾值(0.75)時會觸發checkpoint 刷臟頁操作,同時也會在 MySQL 實例異常宕機后重啟,InnoDB 表數據自動還原恢復過程中使用。


 

用戶讀取或者寫入的最新數據都存儲在 Buffer Pool 中

如果 Buffer Pool 中沒有找到則會讀取物理文件進行查找,之后存儲到 Buffer Pool 中並返回給 MySQL Server。Buffer Pool 采用LRU 機制。

Buffer Pool 決定了一個 SQL 執行的速度快慢,如果查詢結果頁都在內存中則返回結果速度很快,否則會產生物理讀(磁盤讀),返回結果時間變長,性能遠不如存儲在內存中。


「Redo 和 Undo」

Redo log 是一個循環復用的文件集,負責記錄 InnoDB 中所有對 Buffer Pool的物理修改日志,當 Redo log文件空間中,檢查點位置的 LSN 和最新寫入的 LSN 差值(checkpoint_age)達到 Redo log 文件總空間的 75% 后,InnoDB 會進行異步刷新操作,直到降至 75% 以下,並釋放 Redo log 的空間;當 checkpoint_age 達到文件總量大小的 90% 后,會觸發同步刷新,此時 InnoDB 處於掛起狀態無法操作。

Redo記錄變更后的數據。

Undo記錄事務數據變更前的值,用於回滾和其他事務多版本讀。


「ARIES 三原則」

ARIES 三原則,是指 Write Ahead Logging(WAL)。

先寫日志后寫磁盤,日志成功寫入后事務就不會丟失,后續由 checkpoint 機制來保證磁盤物理文件與 Redo 日志達到一致性;

利用 Redo 記錄變更后的數據,即 Redo 記錄事務數據變更后的值;

利用 Undo 記錄變更前的數據,即 Undo 記錄事務數據變更前的值,用於回滾和其他事務多版本讀。

 

===============================

我是Liusy,一個喜歡健身的程序員。

獲取更多干貨以及最新消息,請關注_公_眾_號:上古偽神

如果對您有幫助,點個關注、轉發就是對我最大的支持!!!謝謝


免責聲明!

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



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