MySQLInnoDB儲存引擎(一):精談innodb的儲存結構

一白丁發表於2021-01-05

前言

雖然寫者寫過一期關於innodb的儲存結構和b+樹索引,但當時瞭解太少,所以理解和表達都很淺顯,經過一段漫長時間的研究,對innodb的儲存引擎也瞭解更多一些,所以來分享給大家。

MySQL儲存引擎

儲存引擎在MySQL的體系架構中位於第三層,負責MySQL中的資料的儲存和提取,是與檔案打交道的子系統,它是根據MySQL提供的檔案訪問層抽象介面定製的一種檔案訪問機制,這種機制就叫作儲存引擎。
使用show engines命令,就可以檢視當前資料庫支援的引擎資訊。
在這裡插入圖片描述
在5.5版本之前預設採用MyISAM儲存引擎,從5.5開始採用InnoDB儲存引擎。

  • InnoDB:支援事務,具有提交,回滾和崩潰恢復能力,事務安全
  • MyISAM:不支援事務和外來鍵,訪問速度快
  • Memory:利用記憶體建立表,訪問速度非常快,因為資料在記憶體,而且預設使用Hash索引,但是一旦關閉,資料就會丟失
  • Archive:歸檔型別引擎,僅能支援insert和select語句
  • Csv:以CSV檔案進行資料儲存,由於檔案限制,所有列必須強制指定not null,另外CSV引擎也不支援索引和分割槽,適合做資料交換的中間表
  • BlackHole: 黑洞,只進不出,進來消失,所有插入資料都不會儲存
  • Federated:可以訪問遠端MySQL資料庫中的表。一個本地表,不儲存資料,訪問遠端表內容
  • MRG_MyISAM:一組MyISAM表的組合,這些MyISAM表必須結構相同,Merge表本身沒有資料,對Merge操作可以對一組MyISAM表進行操作。

InnoDB和MyISAM對比

InnoDB和MyISAM是使用MySQL時最常用的兩種引擎型別,我們重點來看下兩者區別。

  • 事務和外來鍵
    InnoDB支援事務和外來鍵,具有安全性和完整性,適合大量insert或update操作
    MyISAM不支援事務和外來鍵,它提供高速儲存和檢索,適合大量的select查詢操作

  • 鎖機制
    InnoDB支援行級鎖,鎖定指定記錄。基於索引來加鎖實現。
    MyISAM支援表級鎖,鎖定整張表。

  • 索引結構
    InnoDB使用聚集索引(聚簇索引),索引和記錄在一起儲存,既快取索引,也快取記錄。
    MyISAM使用非聚集索引(非聚簇索引),索引和記錄分開。

  • 併發處理能力
    MyISAM使用表鎖,會導致寫操作併發率低,讀之間並不阻塞,讀寫阻塞。
    InnoDB讀寫阻塞可以與隔離級別有關,可以採用多版本併發控制(MVCC)來支援高併發

  • 儲存檔案
    InnoDB表對應兩個檔案,一個.frm表結構檔案,一個.ibd資料檔案。InnoDB表最大支援64TB;
    MyISAM表對應三個檔案,一個.frm表結構檔案,一個MYD表資料檔案,一個.MYI索引檔案。從MySQL5.0開始預設限制是256TB。

  • 適用場景
    MyISAM

    • 不需要事務支援(不支援)
    • 併發相對較低(鎖定機制問題)
    • 資料修改相對較少,以讀為主
    • 資料一致性要求不高

    InnoDB

    • 需要事務支援(具有較好的事務特性)
    • 行級鎖定對高併發有很好的適應能力
    • 資料更新較為頻繁的場景
    • 資料一致性要求較高
    • 硬體裝置記憶體較大,可以利用InnoDB較好的快取能力來提高記憶體利用率,減少磁碟IO

    InnoDB儲存結構

    從MySQL 5.5版本開始預設使用InnoDB作為引擎,它擅長處理事務,具有自動崩潰恢復的特性,在日常開發中使用非常廣泛。下面是官方的InnoDB引擎架構圖,主要分為記憶體結構和磁碟結構兩大部分
    在這裡插入圖片描述

InnoDB記憶體結構

記憶體結構主要包括Buffer Pool、Change Buffer、Adaptive Hash Index和Log Buffer四大元件。

Buffer Pool

緩衝池,簡稱BP。BP以Page頁為單位,預設大小16K,BP的底層採用連結串列資料結構管理Page。在InnoDB訪問表記錄和索引時會在Page頁中快取,以後使用可以減少磁碟IO操作,提升效率。

  • Page管理機制
    Page根據狀態可以分為三種型別:

    • free page : 空閒page,未被使用
    • clean page:被使用page,資料沒有被修改過
    • dirty page:髒頁,被使用page,資料被修改過,頁中資料和磁碟的資料產生了不一致

    針對上述三種page型別,InnoDB通過三種連結串列結構來維護和管理:

    • free list :表示空閒緩衝區,管理free page
    • flush list:表示需要重新整理到磁碟的緩衝區,管理dirty page,內部page按修改時間排序。髒頁即存在於flush連結串列,也在LRU連結串列中,但是兩種互不影響,LRU連結串列負責管理page的可用性和釋放,而flush連結串列負責管理髒頁的刷盤操作。
    • lru list:表示正在使用的緩衝區,管理clean page和dirty page,緩衝區以midpoint為基點,前面連結串列稱為new列表區,存放經常訪問的資料,佔63%;後面的連結串列稱為old列表區,存放使用較少資料,佔37%。
  • 改進型LRU演算法維護

    • 普通LRU:末尾淘汰法,新資料從連結串列頭部加入,釋放空間時從末尾淘汰
    • 改性LRU:連結串列分為new和old兩個部分,加入元素時並不是從表頭插入,而是從中間midpoint位置插入,如果資料很快被訪問,那麼page就會向new列表頭部移動,如果資料沒有被訪問,會逐步向old尾部移動,等待淘汰。

    每當有新的page資料讀取到buffer pool時,InnoDb引擎會判斷是否有空閒頁,是否足夠,如果有就將free page從free list列表刪除,放入到LRU列表中。沒有空閒頁,就會根據LRU演算法淘汰LRU連結串列預設的頁,將記憶體空間釋放分配給新的頁。

  • Buffer Pool配置引數

    • show variables like ‘%innodb_page_size%’; //檢視page頁大小
    • show variables like ‘%innodb_old%’; //檢視lru list中old列表引數
    • show variables like ‘%innodb_buffer%’; //檢視buffer pool引數

    建議:將innodb_buffer_pool_size設定為總記憶體大小的60%-80%,innodb_buffer_pool_instances可以設定為多個,這樣可以避免快取爭奪。

Change Buffer

寫緩衝區,簡稱CB。在進行DML操作時,如果BP沒有其相應的Page資料,並不會立刻將磁碟頁載入到緩衝池,而是在CB記錄緩衝變更,等未來資料被讀取時,再將資料合併恢復到BP中。

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

當更新一條記錄時,該記錄在BufferPool存在,直接在BufferPool修改,一次記憶體操作。如果該記錄在BufferPool不存在(沒有命中),會直接在ChangeBuffer進行一次記憶體操作,不用再去磁碟查詢資料,避免一次磁碟IO。當下次查詢記錄時,會先進性磁碟讀取,然後再從ChangeBuffer中讀取資訊合併,最終載入BufferPool中。

寫緩衝區,僅適用於非唯一普通索引頁,為什麼?

如果在索引設定唯一性,在進行修改時,InnoDB必須要做唯一性校驗,因此必須查詢磁碟,做一次IO操作。會直接將記錄查詢到BufferPool中,然後在緩衝池修改,不會在ChangeBuffer操作。

Adaptive Hash Index

自適應雜湊索引,用於優化對BP資料的查詢。InnoDB儲存引擎會監控對錶索引的查詢,如果觀察到建立雜湊索引可以帶來速度的提升,則建立雜湊索引,所以稱之為自適應。InnoDB儲存引擎會自動根據訪問的頻率和模式來為某些頁建立雜湊索引。

Log Buffer

日誌緩衝區,用來儲存要寫入磁碟上log檔案(Redo/Undo)的資料,日誌緩衝區的內容定期重新整理到磁碟log檔案中。日誌緩衝區滿時會自動將其重新整理到磁碟,當遇到BLOB或多行更新的大事務操作時,增加日誌緩衝區可以節省磁碟I/O。

LogBuffer主要是用於記錄InnoDB引擎日誌,在DML操作時會產生Redo和Undo日誌。

LogBuffer空間滿了,會自動寫入磁碟。可以通過將innodb_log_buffer_size引數調大,減少磁碟IO頻率

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

  • 0 : 每隔1秒寫日誌檔案和刷盤操作(寫日誌檔案LogBuffer–>OS cache,刷盤OScache–>磁碟檔案),最多丟失1秒資料
  • 1:事務提交,立刻寫日誌檔案和刷盤,資料不丟失,但是會頻繁IO操作
  • 2:事務提交,立刻寫日誌檔案,每隔1秒鐘進行刷盤操作

InnoDB磁碟結構

InnoDB磁碟主要包含Tablespaces,InnoDB Data Dictionary,Doublewrite Buffer、Redo Log和Undo Logs。

表空間(Tablespaces)

用於儲存表結構和資料。表空間又分為系統表空間、獨立表空間、通用表空間、臨時表空間、Undo表空間等多種型別;

  • 系統表空間(The System Tablespace)
    包含InnoDB資料字典,Doublewrite Buffer,Change Buffer,Undo Logs的儲存區域。系統表空間也預設包含任何使用者在系統表空間建立的表資料和索引資料。系統表空間是一個共享的表空間因為它是被多個表共享的。該空間的資料檔案通過引數innodb_data_file_path控制,預設值是ibdata1:12M:autoextend(檔名為ibdata1、12MB、自動擴充套件)。

  • 獨立表空間(File-Per-Table Tablespaces)
    預設開啟,獨立表空間是一個單表表空間,該表建立於自己的資料檔案中,而非建立於系統表空間中當innodb_file_per_table選項開啟時,表將被建立於表空間中。否則,innodb將被建立於系統表空間中。每個表檔案表空間由一個.ibd資料檔案代表,該檔案預設被建立於資料庫目錄中。表空間的表檔案支援動態(dynamic)和壓縮(commpressed)行格式。

  • 通用表空間(General Tablespaces)
    通用表空間為通過create tablespace語法建立的共享表空間。通用表空間可以建立於mysql資料目錄外的其他表空間,其可以容納多張表,且其支援所有的行格式。

CREATE TABLESPACE ts1 ADD DATAFILE ts1.ibd Engine=InnoDB; //建立表空間ts1
CREATE TABLE t1 (c1 INT PRIMARY KEY) TABLESPACE ts1; //將表新增到ts1表空間
  • 撤銷表空間(Undo Tablespaces)
    撤銷表空間由一個或多個包含Undo日誌檔案組成。在MySQL 5.7版本之前Undo佔用的是System Tablespace共享區,從5.7開始將Undo從System Tablespace分離了出來。InnoDB使用的undo表空間由innodb_undo_tablespaces配置選項控制,預設為0。引數值為0表示使用系統表空間ibdata1;大於0表示使用undo表空間undo_001、undo_002等。

  • 臨時表空間(Temporary Tablespaces)
    分為session temporary tablespaces 和global temporary tablespace兩種。session temporary tablespaces 儲存的是使用者建立的臨時表和磁碟內部的臨時表。global temporary tablespace儲存使用者臨時表的回滾段(rollback segments )。mysql伺服器正常關閉或異常終止時,臨時表空間將被移除,每次啟動時會被重新建立。

資料字典(InnoDB Data Dictionary)

InnoDB資料字典由內部系統表組成,這些表包含用於查詢表、索引和表欄位等物件的後設資料。後設資料物理上位於InnoDB系統表空間中。由於歷史原因,資料字典後設資料在一定程度上與InnoDB表後設資料檔案(.frm檔案)中儲存的資訊重疊。

雙寫緩衝區(Doublewrite Buffer)

位於系統表空間,是一個儲存區域。在BufferPage的page頁重新整理到磁碟真正的位置前,會先將資料存在Doublewrite 緩衝區。如果在page頁寫入過程中出現作業系統、儲存子系統或mysqld程式崩潰,InnoDB可以在崩潰恢復期間從Doublewrite 緩衝區中找到頁面的一個好備份。在大多數情況下,預設情況下啟用雙寫緩衝區,要禁用Doublewrite 緩衝區,可以將innodb_doublewrite設定為0。使用Doublewrite 緩衝區時建議將innodb_flush_method設定為O_DIRECT。

重做日誌(Redo Log)

重做日誌是一種基於磁碟的資料結構,用於在崩潰恢復期間更正不完整事務寫入的資料。MySQL以迴圈方式寫入重做日誌檔案,記錄InnoDB中所有對Buffer Pool修改的日誌。當出現例項故障(像斷電),導致資料未能更新到資料檔案,則資料庫重啟時須redo,重新把資料更新到資料檔案。讀寫事務在執行的過程中,都會不斷的產生redo log。預設情況下,重做日誌在磁碟上由兩個名為ib_logfile0和ib_logfile1的檔案物理表示。

撤銷日誌(Undo Logs)

撤消日誌是在事務開始之前儲存的被修改資料的備份,用於例外情況時回滾事務。撤消日誌屬於邏輯日誌,根據每行記錄進行記錄。撤消日誌存在於系統表空間、撤消表空間和臨時表空間中。

新版本結構演變

在這裡插入圖片描述

  • MySQL 5.7 版本

    • 將 Undo日誌表空間從共享表空間 ibdata 檔案中分離出來,可以在安裝 MySQL 時由使用者自行指定檔案大小和數量。
    • 增加了 temporary 臨時表空間,裡面儲存著臨時表或臨時查詢結果集的資料。
    • Buffer Pool 大小可以動態修改,無需重啟資料庫例項。
  • MySQL 8.0 版本

    • 將InnoDB表的資料字典和Undo都從共享表空間ibdata中徹底分離出來了,以前需要ibdata中資料字典與獨立表空間ibd檔案中資料字典一致才行,8.0版本就不需要了。
    • temporary 臨時表空間也可以配置多個物理檔案,而且均為 InnoDB 儲存引擎並能建立索引,這樣加快了處理的速度。
    • 使用者可以像 Oracle 資料庫那樣設定一些表空間,每個表空間對應多個物理檔案,每個表空間可以給多個表使用,但一個表只能儲存在一個表空間中。
    • 將Doublewrite Buffer從共享表空間ibdata中也分離出來了。

    總結

    本節對innodb引擎介紹開個頭,主要介紹一下innodb的儲存結構和版本變更的差異。

相關文章