1. 程式人生 > 其它 >InnoDB資料頁的結構(FIL_PAGE_INDEX)

InnoDB資料頁的結構(FIL_PAGE_INDEX)

InnoDB存放著各種各樣的頁(page),這些page也分為各種不同的型別。如:FIL_PAGE_INDEX資料頁(帶有真實資料的)、FIL_PAGE_TYPE_ALLOCATED未分配頁、FIL_PAGE_TYPE_BLOG溢位頁……等十多個型別的頁,用於不同的用途。 補充:頁面管理的分類:
  • 空閒頁:空閒頁
  • 資料頁:乾淨頁
  • 髒頁:跟磁碟資料不一致,需要生效
續上: 我們最首先要了解的應該是資料頁——FIL_PAGE_INDEX,因為這裡存放著我們索引和真實的資料。 資料頁的結構大致分為7個部分,有些部份是固定大小的,有些部份是不固定的
  1. File Header:檔案頭部,38位元組
  2. Page Header:頁面頭部,56位元組
  3. Infimum+Superman:頁面中的最小記錄+最大記錄。是虛擬記錄
  4. User Records:使用者記錄,真實資料
  5. Free Page:空閒空間,尚未使用的空間
  6. Page Directory:頁目錄
  7. File Trailer:檔案尾
Page Header:資料頁中的記錄的狀態資訊,如記錄的數量,空閒空間相關資訊、槽的數量等 File Header:存放頁的通用資訊,所有型別的頁都有這個。如頁的編號、上下頁的地址 File Trailer:4位元組的校驗和+4位元組的LSN後4位元組。均做校驗使用,在資料修改後寫入到磁碟前計算出來,在寫入磁碟後核對一遍,校驗和不一致則寫入期間發生錯誤 除了主要儲存使用者記錄的user records,最重要的就是page dirctory頁目錄了,因為關係到記錄查詢的效率。 頁目錄規則:(圖)
  • 所有未刪除的記錄將被劃分為若干個分組,包括偽記錄
  • 每個組的最後一條記錄的n_owned屬性標記著當前分組的記錄數量
  • 每個組的最後一條記錄在頁面中的地址偏移量提取出來,按順序放置到靠近頁尾的地方,這個地方就稱之為頁目錄(Page Directory),這些偏移量也稱為“槽”,每個槽佔2位元組,頁目錄就是由多個槽組成的。
分組的規則:
  • Infimum所在的分組,只能有Infimum記錄本身,即1條記錄
  • Superman所在的分組記錄條數只能在1~8條之間,剩下其他分組條數範圍在4~8條之間
  • 分組實現步驟:
    1. 初始狀態,一個page只有兩條記錄,兩個分組,兩個槽,分別記錄Infimum和Superman的地址偏移量
    2. 每插入一條記錄,都會從頁目錄中找到對應記錄的主鍵值比待插入記錄的主鍵值大且差值最小的槽(沒懂),然後把該槽對應的記錄的n_owned值+1,表示本組又添加了一條記錄,直至本組記錄數等於8
    3. 當一個組的記錄數等於8後,再插入一條記錄時,會講組中的記錄拆分成兩個組,其中一個組4條記錄,一個組5條記錄。拆分過程中會在頁目錄中新增一個槽,記錄這個新增分組中最大的那個記錄的偏移量
使用頁目錄查詢記錄 我們知道mysql的底層是B+樹,樹的葉子節點由一個個page連線而成的連結串列組成,當查詢資料時,我們通過高效能的B+樹拿到了我們所需要的page,而在單個page中,資料量已經小很多了,利用簡單的二分查詢就可以將記錄拿到。 假設我們拿到一條記錄的主鍵,根據主鍵去查詢當前頁中的記錄:
  1. 初始狀態下,設定low為0,hight為槽的總數量, 槽的數量也就是分組的數量。由於槽本身記錄著其分組最後一條記錄的偏移量,所以槽所對應的主鍵就是槽所在分組的最大主鍵。使用需要查詢的主鍵跟槽對比,修改low和hight兩個變數,最後對比出需要查詢的記錄具體在哪個槽。
  2. 知道某個槽(分組)中存放著我想要的記錄,則需要把這個槽從小到大逐個遍歷,我們拿到的槽的值是不可以用來遍歷的,因為槽的值只是當前分組的最大記錄,不能反向去遍歷。而頁目錄中的槽都是連續存放的,因此我們需要拿到上一個槽,獲取那條記錄的下一條記錄,也就是next_records值,然後遍歷所在分組的所有記錄,即可找到該主鍵對應的記錄。
記錄頭資訊詳解 插入資料的規則: 在資料頁剛生成時是沒有User Recoeds部分的,每插入一條記錄,都會從Free Page中取出一條記錄大小的空間劃給User Records,當空閒空間用盡後還在插入資料,則需要申請新的page。 現在回過頭再看看使用者空間裡,每條記錄所攜帶的額外記錄,裡面的頭資訊放著很多重要的屬性:
  • 預留1、2位:預留位,不用
  • delete_flag:標記是否刪除。mysql中的做記錄刪除並沒有刪除記錄所在佔的空間,而是講該記錄置為1.如果刪除時連帶空間也刪除,則每次刪除一條記錄都需要重新排列其他記錄,造成效能消耗。因此使用該標記。並且所有被刪除的記錄就組成一個垃圾連結串列。記錄在該連結串列中的空間時可重用空間,後續新增資料時,又可能會覆蓋掉記錄,並佔用其空間。
  • min_rec_flag:B+樹中的每層非葉子節點中的最小目錄項記錄會新增該標記
  • n_owned:頁面內的記錄分成的組,每個組中有一個記錄的你n_owned值記錄著當前記錄組的條數,剩餘記錄該值均為0
此項為重點,單獨提出來講:
  • Heap_no:當前記錄在頁面堆中的相對位置。
堆是User Records中的記錄的排列結構。記錄時一條條的排列出來的,(圖)heap_n標記記錄之間的相對位置,前面的值小,後面的值大。值的大小是比較主鍵的大小。 當我們插入新的記錄之前,頁裡面就已經存在兩條記錄了,即Infimum+Superman兩條偽記錄,也叫虛擬記錄。Infimum代表一個頁中最小的記錄,superman代表最大的記錄。這兩條記錄的位置處於user records的最前面,(圖)因此他們的heap_no的值最小。不論插入多少資料,所有使用者記錄的heap_no都比Infimum大,比Superman小。 Infimum+Superman的結構:5位元組的記錄頭資訊+8位元組的固定單片語成。
  • Record_type:記錄型別,普通記錄標記0;非葉節點的目錄標記1,Infimum標記2,Superman標記3
  • Next_records:表示到下一條距離的相對位置。該屬性記錄當前記錄的真實資料到下一條記錄真實資料的距離。如果某條記錄該屬性位正數,則下一條記錄在該記錄的後面。例如:第一條記錄該屬性值為32,則下一條的真實資料地址在本條記錄的真實資料的地址往後+32位元組;如果某條記錄該屬性為負數-145,則下一條記錄的真實資料需要往前-145位元組。
不難看出,所有記錄的next_records屬性值形成了一個單向連結串列,這個連結串列有最小值Infimum和最大值Superman。當中間的使用者記錄被刪除時,所刪除的記錄deleted_flag屬性置為1,next_records屬性置為0,前一條記錄的next_records值更改為被刪除記錄的下一條記錄的真實資料地址。(如果剛刪除接著又插入回去,會複用剛才的空間,不會申請新的空間)