天天看點

mysql的innodb引擎是如何存儲資料的?

作者:愛釣魚的代碼搬運工
  • 前言

Mysql的資料是以檔案的形式持久化存儲在磁盤上的,當我們讀取資料的時候,mysql存儲引擎就會從這些檔案中拿出資料傳回給我們,當我們更新寫入資料的時mysql存儲引擎就會把資料寫回這些檔案系統 ,那innodb存儲引擎到底是如何組織這些資料的呢?

  • 資料庫資料檔案存儲位置

mysql的資料是存儲在mysql資料目錄中的,我們可以在指令行使用 "show variables like 'datadir' " 來檢視資料目錄的位置:

mysql的innodb引擎是如何存儲資料的?

在資料目錄下我們可以看到,每一個資料庫都有一個與之對應的同名檔案夾

mysql的innodb引擎是如何存儲資料的?

mysql 表空間(TableSpace)是指在Mysql資料庫中用來存儲表資料和索引的區域。表空間從管理上可以分為:系統表空間、獨立表空間、撤銷表空間和臨時表空間。

資料目錄下的ibdata1 檔案為系統表空間檔案,在每個資料庫自己的目錄中每一張表都對應兩個檔案:

tablename.ibd 獨立表空間檔案

tablename.frm 表描述檔案

mysql的innodb引擎是如何存儲資料的?

.ibd 字尾的檔案是每張表對應的獨立表空間,獨立表空間的主要優勢是可以實作更好的性能和更高的可靠性。由于每個表都有自己的資料檔案,是以可以單獨進行備份和恢複操作,而不會影響其他表的資料。此外,由于每個表的資料和索引都存儲在單獨的檔案中,是以可以更快速地進行查詢和更新操作。

資料到底是存儲在獨立表空間還是系統表空間,可在資料庫啟動時設定innodb_file_per_table 來控制。

  • mysql 表空間檔案解析

mysql資料存儲分為5個層次結構:表空間→段→簇→頁→行,他們之間的關系如下:

  1. 表空間(Tablespace):表空間是邏輯上的一個容器,用于存儲表和索引的資料。一個表空間可以包含多個段。
  2. 段(Segment):段是MySQL中存儲資料的基本機關,它是由連續的簇組成的邏輯存儲結構。一個段可以包含一個或多個簇。
  3. 簇(Extent):簇是段擴充的最小機關,也叫做區,每個區的大小是1M(64個連續的頁)。一個簇通常包含多個頁。
  4. 頁(Page):頁是innodb對磁盤管理的最小機關,每個資料頁的大小為16kb,它是用于存儲資料的固定大小的塊。頁的大小可以根據存儲引擎和配置進行調整。
  5. 行(Row):行是表中的一條記錄,它包含了一組字段(列)的資料。行存儲在頁中,每個頁可以包含多條行。

系統表空間通常由以下段組成:

  1. 資料段(Data Segment):存儲表的實時資料,每個系統表空間可以包含一個或多個資料段,其中的每個資料段可以對應一張表或者多個表的資料。
  2. 索引段(Index Segment):索引段存儲了表的索引資料。MySQL(innodb引擎)使用B+樹索引結構,每個索引對應一個索引段。
  3. 復原段(Rollback Segment):復原段用于實作事務的復原操作。當事務需要復原時,相關的修改操作可以通過復原段進行撤銷。
  4. 系統段(System Segment):系統段包含了InnoDB存儲引擎的中繼資料資訊,如表空間的相關配置和統計資訊。

獨立表空間由以下段組成:

  1. 資料段:每個獨立表空間都至少包含一個資料段,用于存儲表的資料。資料段中包含了表的行資料,包括表的所有列和資料行的中繼資料資訊。如果表使用了壓縮功能,資料段中還會包含壓縮後的資料。
  2. 索引段:每個表可以有多個索引,每個索引都有一個對應的索引段。索引段中包含了索引的所有鍵值和指向資料行的指針,用于支援快速的索引查詢操作。
  3. 復原段(Rollback Segment):復原段用于實作事務的復原操作。當事務需要復原時,相關的修改操作可以通過復原段進行撤銷。
  4. 描述符段(Descriptor Segment):描述符段存儲了關于獨立表空間的描述資訊,包括表空間的結構和屬性等。

頁的資料結構為:

mysql的innodb引擎是如何存儲資料的?

頁的頭部儲存了兩個指針,分别指向前一個頁和後一個頁,頭部還有頁的類型資訊和用來唯一辨別頁的編号。根據這個指針分布可以想象到頁連結起來就是一個雙向連結清單。雙向連結清單之間的實體位置可能會離得非常遠,當遇到範圍查詢的适用場景的時候,就會定位到最左邊和最右邊的記錄,然後沿着雙向連結清單一直掃描,而如果這其中的兩個頁面實體上離得特别遠,就會成為随機I/O,由于磁盤和記憶體的速度相差了幾個數量級(磁盤尋道、半圈旋轉、資料傳輸),是以随機I/O的速度是非常慢的,是以應該盡量讓連結清單中邏輯上相鄰的頁實體上也相鄰,這樣進行範圍查詢的時候就可以使用順序I/O。

為了盡量讓連結清單中邏輯上相鄰的頁實體上也相鄰,是以引入區的概念,一個區就是實體上連續的64個頁,即64*16=1MB。并且在資料量較大的時候,會以區為機關為索引配置設定空間而不是以頁為基本機關,甚至資料特别多的時候,會一次性配置設定多個連續的區,雖然可能造成空間浪費,但是消除了很多的随機I/O,大大地提高了性能。

一開始生成頁的時候并沒有User Records這個部分。每當我們插⼊⼀條記錄,都會從Free Space部分,也就是尚未使⽤的存儲空間中申請⼀個記錄⼤⼩的空間劃分到User Records部分,當Free Space部分的空間全部被User Records部分替代掉之後,也就意味着這個頁使⽤完了,如果還有新的記錄插⼊的話,就需要去申請新的頁了。

  • 總結

建立一個索引,通常會對應一個B+樹,也會産生兩個段,一個是資料段,一個是索引段,資料段即對應B+數的葉子節點段,索引段即對應B+樹的非葉子節點段。段并不是實體上連續的區,而是一個邏輯的資料塊,由若幹個零散的頁面和一些完整的區組成的。B+樹中的節點與段之間的對應關系是多對多的關系。一個段可以包含多個B+樹節點,而一個B+樹節點也可以跨越多個段。每個區又是實體上連續的64個頁組成,頁和頁之間又是雙向連結清單。

一個很簡單的B+樹如下所示:

mysql的innodb引擎是如何存儲資料的?

繼續閱讀