mysql page_一文理解MySQL中的page頁--轉
在介紹InnoDB中的頁的時候,很有必要先讓大家瞭解一下InnoDB中的儲存結構
從InnoDB儲存引擎的邏輯結構看,所有資料都被邏輯地存放在一個空間內,稱為表空間(tablespace),而表空間由段(sengment)、區(extent)、頁(page)組成。 在一些文件中extend又稱塊(block)。
一、表空間(table space)
表空間(Tablespace)是一個邏輯容器,表空間儲存的物件是段,在一個表空間中可以有一個或多個段,但是一個段只能屬於一個表空間。資料庫由一個或多個表空間組成,表空間從管理上可以劃分為系統表空間、使用者表空間、撤銷表空間、臨時表空間等。
在 InnoDB 中存在兩種表空間的型別:共享表空間和獨立表空間。如果是共享表空間就意味著多張表共用一個表空間。如果是獨立表空間,就意味著每張表有一個獨立的表空間,也就是資料和索引資訊都會儲存在自己的表空間中。獨立的表空間可以在不同的資料庫之間進行遷移。可通過命令
mysql > show variables like 'innodb_file_per_table';
檢視當前系統啟用的表空間型別。目前最新版本已經預設啟用獨立表空間。
InnoDB把資料儲存在表空間內,表空間可以看作是InnoDB儲存引擎邏輯結構的最高層。本質上是一個由一個或多個磁碟檔案組成的虛擬檔案系統。InnoDB用表空間並不只是儲存表和索引,還儲存了回滾段、雙寫緩衝區等。
二、段(segment)
段(Segment)由一個或多個區組成,區在檔案系統是一個連續分配的空間(在 InnoDB 中是連續的 64 個頁),不過在段中不要求區與區之間是相鄰的。段是資料庫中的分配單位,不同型別的資料庫物件以不同的段形式存在。當我們建立資料表、索引的時候,就會相應建立對應的段,比如建立一張表時會建立一個表段,建立一個索引時會建立一個索引段。
三、區(extent)
在 InnoDB 儲存引擎中,一個區會分配 64 個連續的頁。因為 InnoDB 中的頁大小預設是 16KB,所以一個區的大小是 64*16KB=1MB。在任何情況下每個區大小都為1MB,為了保證頁的連續性,InnoDB儲存引擎每次從磁碟一次申請4-5個區。預設情況下,InnoDB儲存引擎的頁大小為16KB,即一個區中有64個連續的頁。
四、頁(Page)
頁是InnoDB儲存引擎磁碟管理的最小單位,每個頁預設16KB;InnoDB儲存引擎從1.2.x版本礙事,可以通過引數innodb_page_size將頁的大小設定為4K、8K、16K。若設定完成,則所有表中頁的大小都為innodb_page_size,不可以再次對其進行修改,除非通過mysqldump匯入和匯出操作來產生新的庫。
innoDB儲存引擎中,常見的頁型別有:
1. 資料頁(B-tree Node)
2. undo頁(undo Log Page)
3. 系統頁 (System Page)
4. 事物資料頁 (Transaction System Page)
5. 插入緩衝點陣圖頁(Insert Buffer Bitmap)
6. 插入緩衝空閒列表頁(Insert Buffer Free List)
7. 未壓縮的二進位制大物件頁(Uncompressed BLOB Page)
8. 壓縮的二進位制大物件頁 (compressed BLOB Page)
五、行(row)
InnoDB儲存引擎是按行進行存放的,每個頁存放的行記錄也是有硬性定義的,最多允許存放16KB/2-200,即7992行記錄。
瞭解了整體架構,下面我們開始詳細對Page來做一些介紹。
先貼一張Page完整的結構圖
Innodb引擎中的Page完整結構圖(注意箭頭)
上較的概念實在太多了,為了方便理解,可以按下面的分解一下Page的結構
Page結構示意圖1
每部分的意義
Page結構示意圖2
頁結構整體上可以分為三大部分,分別為通用部分(檔案頭、檔案尾)、儲存記錄空間、索引部分。
第一部分通用部分,主要指檔案頭和檔案尾,將頁的內容進行封裝,通過檔案頭和檔案尾校驗的CheckSum方式來確保頁的傳輸是完整的。
在檔案頭中有兩個欄位,分別是 FIL_PAGE_PREV 和 FIL_PAGE_NEXT,它們的作用相當於指標,分別指向上一個資料頁和下一個資料頁。連線起來的頁相當於一個雙向的連結串列,如下圖所示:
需要說明的是採用連結串列的結構讓資料頁之間不需要是物理上的連續,而是邏輯上的連續。
第二個部分是記錄部分,頁的主要作用是儲存記錄,所以“最小和最大記錄”和“使用者記錄”部分佔了頁結構的主要空間。另外空閒空間是個靈活的部分,當有新的記錄插入時,會從空閒空間中進行分配用於儲存新記錄,如下圖所示:
Page結構示意圖3
一個頁內必須儲存2行記錄,否則就不是B+tree,而是連結串列了。
第三部分是索引部分,這部分重點指的是頁目錄(示意圖2中的s0-sn),它起到了記錄的索引作用,因為在頁中,記錄是以單向連結串列的形式進行儲存的。單向連結串列的特點就是插入、刪除非常方便,但是檢索效率不高,最差的情況下需要遍歷連結串列上的所有節點才能完成檢索,因此在頁目錄中提供了二分查詢的方式,用來提高記錄的檢索效率。這個過程就好比是給記錄建立了一個目錄:
將所有的記錄分成幾個組,這些記錄包括最小記錄和最大記錄,但不包括標記為“已刪除”的記錄。
第 1 組,也就是最小記錄所在的分組只有 1 個記錄;
最後一組,就是最大記錄所在的分組,會有 1-8 條記錄;
其餘的組記錄數量在 4-8 條之間。
這樣做的好處是,除了第 1 組(最小記錄所在組)以外,其餘組的記錄數會盡量平分。
在每個組中最後一條記錄的頭資訊中會儲存該組一共有多少條記錄,作為 n_owned 欄位。
頁目錄用來儲存每組最後一條記錄的地址偏移量,這些地址偏移量會按照先後順序儲存起來,每組的地址偏移量也被稱之為槽(slot),每個槽相當於指標指向了不同組的最後一個記錄。如下圖所示:
頁目錄儲存的是槽,槽相當於分組記錄的索引。我們通過槽查詢記錄,實際上就是在做二分查詢。這裡我以上面的圖示進行舉例,5 個槽的編號分別為 0,1,2,3,4,我想查詢主鍵為 9 的使用者記錄,我們初始化查詢的槽的下限編號,設定為 low=0,然後設定查詢的槽的上限編號 high=4,然後採用二分查詢法進行查詢。
首先找到槽的中間位置 p=(low+high)/2=(0+4)/2=2,這時我們取編號為 2 的槽對應的分組記錄中最大的記錄,取出關鍵字為 8。因為 9 大於 8,所以應該會在槽編號為 (p,high] 的範圍進行查詢
接著重新計算中間位置 p’=(p+high)/2=(2+4)/2=3,我們查詢編號為 3 的槽對應的分組記錄中最大的記錄,取出關鍵字為 12。因為 9 小於 12,所以應該在槽 3 中進行查詢。
遍歷槽 3 中的所有記錄,找到關鍵字為 9 的記錄,取出該條記錄的資訊即為我們想要查詢的內容。
B+ 樹是如何進行記錄檢索的?
如果通過 B+ 樹的索引查詢行記錄,首先是從 B+ 樹的根開始,逐層檢索,直到找到葉子節點,也就是找到對應的資料頁為止,將資料頁載入到記憶體中,頁目錄中的槽(slot)採用二分查詢的方式先找到一個粗略的記錄分組,然後再在分組中通過連結串列遍歷的方式查詢記錄。
————————————————
版權宣告:本文為CSDN博主「柯必Da」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/weixin_26786277/article/details/113121272