天天看點

資料頁内部結構

起步

  • 頁的概念
頁的大小預設為16k
以頁作為磁盤和記憶體之間互動的基本機關,也就是一次最少從磁盤中讀取16KB的内容到記憶體中,一次最少把記憶體中的16KB内容重新整理到磁盤中。也就是說,在資料庫中,不論讀一行,還是讀多行,
都是将這些行所在的頁進行加載。也就是說,資料庫管理存儲空間的基本機關是頁(Page),資料庫I/o操作的最小機關是頁。一個頁中可以存儲多個行記錄      
  • 頁結構
頁a、頁b、頁c…頁n這些頁可以不在實體結構上相連,隻要通過雙向連結清單相關聯即可。每個資料頁中的記錄會按照主鍵值從小到大的順序組成一個單向連結清單,每個資料頁都會為存儲
在它裡邊的記錄生成一個頁目錄,在通過主鍵查找某條記錄的時候可以在頁目錄中使用二分法快速定位到對應的槽,然後再周遊該槽對應分組中的記錄即可快速找到指定的記錄      
  • 頁的上層結構
  • 資料頁内部結構
區(Extent):是比頁大一級的存儲結構,在InnoDB存儲引擎中,一個區會配置設定64個連續的頁。因為InnoDB中的頁大小預設是16KB,是以一個區的大小是64*16KB=1MB

段(Segment):由一個或多個區組成,區在檔案系統是一個連續配置設定的空間(在InnoDB中是連續的64個頁),不過在段中不要求區與區之間是相鄰的。段是資料庫中的配置設定機關,
不同類型的資料庫對象以不同的段形式存在。當我們建立資料表、索引的時候,就會相應建立對應的段,比如建立一張表時會建立一個表段,建立一個索引時會建立一個索引段

表空間(Tablespace):是一個邏輯容器,表空間存儲的對象是段,在一個表空間中可以有一個或多個段,但是一個段隻能屬于一個表空間。資料庫由一個或多個表空間組成,
表空間從管理上可以劃分為系統表空間、使用者表空間、撤銷表空間、臨時表空間等。      
  • 頁的分類
資料頁(儲存B+樹節點)
系統頁
Undo頁
事務資料頁      
  • 頁結構
  • 資料頁内部結構
  • 大綱
  • 資料頁内部結構
  • 簡介
  • 資料頁内部結構

File Header

描述各種頁的通用資訊。(比如頁的編号、其上一頁、下一頁是誰等)      
  • 構成
  • 資料頁内部結構
  • FIL_PAGE_OFFSET
每一個頁都有一個單獨的頁号,就跟你的身份證号碼一樣,InnoDB通過頁号可以唯一定位一個頁。      
  • FIL_PAGE_TYPE
  • 資料頁内部結構
  • FIL_PAGE_PREV和FIL_PAGE_NEXT
InnoDB都是以頁為機關存放資料的,如果資料分散到多個不連續的頁中存儲的話需要把這些頁關聯起來,
FIL_PAGE_PREV和FIL_PAGE_NEXT就分别代表本頁的上一個和下一個頁的頁号。這樣通過建立一個雙向連結清單把許許多多的頁就都串聯起來了,
保證這些頁之間不需要是實體上的連續,而是邏輯上的連續      
資料頁内部結構
資料頁内部結構
  • FIL_PAGE_SPACE_OR_CHKSUM
# 代表目前頁面的校驗和(checksum)

# 校驗和
就是對于一個很長的位元組串來說,我們會通過某種算法來計算一個比較短的值來代表這個很長的位元組串,這個比較短的值就稱為校驗和。
在比較兩個很長的位元組串之前,先比較這兩個長位元組串的校驗和,如果校驗和都不一樣,則兩個長位元組串肯定是不同的,是以省去了直接比較
兩個比較長的位元組串的時間損耗

# 作用:
InnoDB存儲引擎以頁為機關把資料加載到記憶體中處理,如果該頁中的資料在記憶體中被修改了,那麼在修改後的某個時間需要把資料同步到磁盤中。
但是在同步了一半的時候斷電了,造成了該頁傳輸的不完整
為了檢測一個頁是否完整(也就是在同步的時候有沒有發生隻同步一半的尴尬情況),這時可以通過檔案尾的校驗和(checksum 值)與檔案頭的校驗和做比對,
如果兩個值不相等則證明頁的傳輸有問題,需要重新進行傳輸,否則認為頁的傳輸已經完成

# 具體的:
每當一個頁面在記憶體中修改了,在同步之前就要把它的校驗和算出來,因為File Header在頁面的前邊,是以校驗和會被首先同步到磁盤,當完全寫完時,
校驗和也會被寫到頁的尾部,如果完全同步成功,則頁的首部和尾部的校驗和應該是一緻的。如果寫了一半兒斷電了,那麼在File Header中的校驗和就
代表着已經修改過的頁,而在File Trailer中的校驗和代表着原先的頁,二者不同則意味着同步中間出了錯。這裡,校驗方式就是采用 Hash 算法進行校驗。      
  • FIL_PAGE_LSN
頁面被最後修改時對應的日志序列位置(英文名是:Log Sequence Number)      

File Trailer

前4個位元組代表頁的校驗和:
這個部分是和File Header中的校驗和相對應的。
 
後4個位元組代表頁面被最後修改時對應的日志序列位置(LSN):
這個部分也是為了校驗頁的完整性的,如果首部和尾部的LSN值校驗不成功的話,就說明同步過程出現了問題      

Free Space

我們自己存儲的記錄會按照指定的行格式存儲到User Records部分。但是在一開始生成頁的時候,其實并沒有User Records這個部分,每當我們插入一條記錄,
都會從Free Space部分,也就是尚未使用的存儲空間中申請一個記錄大小的空間劃分到User Records部分,當Free Space部分的空間全部被User Records部分
替代掉之後,也就意味着這個頁使用完了,如果還有新的記錄插入的話,就需要去申請新的頁了。      
資料頁内部結構

User Records

User Records中的這些記錄按照指定的行格式一條一條擺在User Records部分,互相之間形成單連結清單。      

Infimum + Supremum

記錄可以比大小,對于一條完整的記錄來說,比較記錄的大小就是比較主鍵的大小。比方說我們插入的4行記錄的主鍵值分别是:1、2、3、4,
這也就意味着這4條記錄是從小到大依次遞增。
 
InnoDB規定的最小記錄與最大記錄這兩條記錄的構造十分簡單,都是由5位元組大小的記錄頭資訊和8位元組大小的一個固定的部分組成的,如圖所示:      
資料頁内部結構
這兩條記錄不是我們自己定義的記錄,是以它們并不存放在頁的User Records部分,他們被單獨放在一個稱為Infimum + Supremum的部分,如圖所示:      
資料頁内部結構

page directory(頁目錄)

在頁中,記錄是以單向連結清單的形式進行存儲的。單向連結清單的特點就是插入、删除非常友善,但是檢索效率不高,最差的情況下需要周遊連結清單上的所有節點
才能完成檢索。是以在頁結構中專門設計了頁目錄這個子產品,專門給記錄做一個目錄,通過二分查找法的方式進行檢索,提升效率

需求:根據主鍵值查找頁中的某條記錄,如何實作快速查找呢?
SELECT * FROM page_demo WHERE c1 = 3;

方式1:順序查找
從Infimum記錄(最小記錄)開始,沿着連結清單一直往後找,總有一天會找到(或者找不到),在找的時候還能投機取巧,因為連結清單中各個記錄的值是按照
從小到大順序排列的,是以當連結清單的某個節點代表的記錄的主鍵值大于你想要查找的主鍵值時,你就可以停止查找了,因為該節點後邊的節點的主鍵值依次遞增
如果一個頁中存儲了非常多的記錄,這麼查找性能很差。
 
方式2:使用頁目錄,二分法查找
1. 将所有的記錄分成幾個組,這些記錄包括最小記錄和最大記錄,但不包括标記為“已删除”的記錄。
2. 第 1 組,也就是最小記錄所在的分組隻有 1 個記錄;
   最後一組,就是最大記錄所在的分組,會有 1-8 條記錄;
   其餘的組記錄數量在 4-8 條之間。
這樣做的好處是,除了第 1 組(最小記錄所在組)以外,其餘組的記錄數會盡量平分。
3. 在每個組中最後一條記錄的頭資訊中會存儲該組一共有多少條記錄,作為 n_owned 字段。
4. 頁目錄用來存儲每組最後一條記錄的位址偏移量,這些位址偏移量會按照先後順序存儲起來,每組的位址偏移量也被稱之為槽(slot),
每個槽相當于指針指向了不同組的最後一個記錄。      

page header(頁面頭部)

為了能得到一個資料頁中存儲的記錄的狀态資訊,比如本頁中已經存儲了多少條記錄,第一條記錄的位址是什麼,頁目錄中存儲了多少個槽等等,特意在頁中定義了一個叫Page Header的部分,這個部分占用固定的56個位元組,專門存儲各種狀态資訊。      
資料頁内部結構
假如新插入的一條記錄的主鍵值比上一條記錄的主鍵值大,我們說這條記錄的插入方向是右邊,反之則是左邊。用來表示最後一條記錄插入方向的
狀态就是PAGE_DIRECTION

假設連續幾次插入新記錄的方向都是一緻的,InnoDB會把沿着同一個方向插入記錄的條數記下來,這個條數就用PAGE_N_DIRECTION這個狀态表示。
當然,如果最後一條記錄的插入方向改變了的話,這個狀态的值會被清零重新統計。