1. 記錄頭信息
上一篇博客說到每行記錄都會有記錄頭信息,用來記錄每一行的一些屬性
Compact行記錄的記錄頭信息為例
1.1 delete_mask
這個屬性標記著當前記錄是否被刪除,占用1個二進制位,值為0的時候代表記錄并沒有被刪除,為1的時候代表記錄被刪除掉了。
這些被刪除的記錄之所以不立即從磁盤上移除,是因為移除它們之后把其他的記錄在磁盤上重新排列需要性能消耗,所以只是打一個刪除標記而已,所有被刪除掉的記錄都會組成一個所謂的垃圾鏈表,在這個鏈表中的記錄占用的空間稱之為所謂的可重用空間,之后如果有新記錄插入到表中的話,可能把這些被刪除的記錄占用的存儲空間覆蓋掉。
1.2 min_rec_mask
B+樹的每層非葉子節點中的最小記錄都會添加該標記
1.3 heap_no
這個屬性表示當前記錄在本頁中的位置。InnoDB會自動給每個頁添加兩條偽記錄分別為最小記錄與最大記錄。這兩條記錄的構造十分簡單,都是由5字節大小的記錄頭信息和8字節大小的一個固定的部分組成的。由于這兩條記錄不是我們自己定義的記錄,所以它們并不存放在頁的User Records部分,他們被單獨放在一個稱為Infimum + Supremum的部分。
1.4 record_type
這個屬性表示當前記錄的類型,一共有4種類型的記錄,0表示普通記錄,1表示B+樹非葉節點記錄,2表示最小記錄,3表示最大記錄。
1.5 next_record
它表示從當前記錄的真實數據到下一條記錄的真實數據的地址偏移量。不論我們怎么對頁中的記錄做增刪改操作,InnoDB始終會維護一條記錄的單鏈表,鏈表中的各個節點是按照主鍵值由小到大的順序連接起來的。
2. Page Directory(頁目錄)
Page Directory(頁目錄)中存放了記錄的相對位置(注意,這里存放的是頁相對位置,而不是偏移量),有些時候這些記錄指針稱為Slots(槽)或者目錄槽(Directory Slots)。與其他數據庫系統不同的是,InnoDB并不是每個記錄擁有一個槽,InnoDB存儲引擎的槽是一個稀疏目錄(sparse directory),即一個槽中可能屬于(belong to)多個記錄,最少屬于4條記錄,最多屬于8條記錄。
Slots中記錄按照鍵順序存放,這樣可以利用二叉查找迅速找到記錄的指針。假設我們有(‘i’,‘d’,‘c’,‘b’,‘e’,‘g’,‘l’,‘h’,‘f’,‘j’,‘k’,‘a’),同時假設一個槽中包含4條記錄,則Slots中的記錄可能是(‘a’,‘e’,‘i’)。
由于InnoDB存儲引擎中Slots是稀疏目錄,二叉查找的結果只是一個粗略的結果,所以InnoDB必須通過recorder header中的next_record來繼續查找相關記錄。同時,slots很好地解釋了recorder header中的n_owned值的含義,即還有多少記錄需要查找,因為這些記錄并不包括在slots中。
需要牢記的是,B+樹索引本身并不能找到具體的一條記錄,B+樹索引能找到只是該記錄所在的頁。數據庫把頁載入內存,然后通過Page Directory再進行二叉查找。只不過二叉查找的時間復雜度很低,同時內存中的查找很快,因此通常我們忽略了這部分查找所用的時間。
3.File Header(文件頭部)
Page Header是專門針對數據頁記錄的各種狀態信息,比方說頁里頭有多少個記錄了呀,有多少個槽了呀。File Header針對各種類型的頁都通用,也就是說不同類型的頁都會以File Header作為第一個組成部分,它描述了一些針對各種頁都通用的一些信息,比方說這個頁的編號是多少,它的上一個頁、下一個頁是誰。
File Header用來記錄頁的一些頭信息,由如下8個部分組成,共占用38個字節
-
FIL_PAGE_SPACE_OR_CHKSUM:當MySQL版本小于MySQL-4.0.14,該值代表該頁屬于哪個表空間,因為如果我們沒有開啟innodb_file_per_table,共享表空間中可能存放了許多頁,并且這些頁屬于不同的表空間。之后版本的MySQL,該值代表頁的checksum值(一種新的checksum值)。
-
FIL_PAGE_OFFSET:表空間中頁的偏移值。
-
FIL_PAGE_PREV,FIL_PAGE_NEXT:當前頁的上一個頁以及下一個頁。B+Tree特性決定了葉子節點必須是雙向列表。
-
FIL_PAGE_LSN:該值代表該頁最后被修改的日志序列位置LSN(Log Sequence Number)。
-
FIL_PAGE_TYPE:頁的類型。通常有以下幾種,見表4-4。請記住0x45BF,該值代表了存放的數據頁。
-
FIL_PAGE_FILE_FLUSH_LSN:該值僅在數據文件中的一個頁中定義,代表文件至少被更新到了該LSN值。
-
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID:從MySQL 4.1開始,該值代表頁屬于哪個表空間。
4.Page Header
接著File Header部分的是Page Header,用來記錄數據頁的狀態信息
為了能得到一個數據頁中存儲的記錄的狀態信息,比如本頁中已經存儲了多少條記錄,第一條記錄的地址是什么,頁目錄中存儲了多少個槽等等,在頁中定義了一個叫Page Header的部分,它是頁結構的第二部分,這個部分占用固定的56個字節,專門存儲各種狀態信息
-
PAGE_N_DIR_SLOTS:在Page Directory(頁目錄)中的Slot(槽)數。Page Directory會在后面介紹。
-
PAGE_HEAP_TOP:堆中第一個記錄的指針。
-
PAGE_N_HEAP:堆中的記錄數。
-
PAGE_FREE:指向空閑列表的首指針。
-
PAGE_GARBAGE:已刪除記錄的字節數,即行記錄結構中,delete flag為1的記錄大小的總數。
-
PAGE_LAST_INSERT:最后插入記錄的位置。
-
PAGE_DIRECTION:最后插入的方向。可能的取值為PAGE_LEFT(0x01),PAGE_RIGHT(0x02),PAGE_SAME_REC(0x03),PAGE_SAME_PAGE(0x04),PAGE_NO_DIRECTION(0x05)。
-
PAGE_N_DIRECTION:一個方向連續插入記錄的數量。
-
PAGE_N_RECS:該頁中記錄的數量。
-
PAGE_MAX_TRX_ID:修改當前頁的最大事務ID,注意該值僅在Secondary Index定義。
-
PAGE_LEVEL:當前頁在索引樹中的位置,0x00代表葉節點。
-
PAGE_INDEX_ID:當前頁屬于哪個索引ID。
-
PAGE_BTR_SEG_LEAF:B+樹的葉節點中,文件段的首指針位置。注意該值僅在B+樹的Root頁中定義。
-
PAGE_BTR_SEG_TOP:B+樹的非葉節點中,文件段的首指針位置。注意該值僅在B+樹的Root頁中定義。
5.File Trailer
為了保證頁能夠完整地寫入磁盤(如可能發生的寫入過程中磁盤損壞、機器宕機等原因),InnoDB存儲引擎的頁中設置了File Trailer部分。
File Trailer部分,這個部分由8個字節組成,可以分成2個小部分:
- 前4個字節代表頁的校驗和
這個部分是和File Header中的校驗和相對應的。每當一個頁面在內存中修改了,在同步之前就要把它的校驗和算出來,因為File Header在頁面的前邊,所以校驗和會被首先同步到磁盤,當完全寫完時,校驗和也會被寫到頁的尾部,如果完全同步成功,則頁的首部和尾部的校驗和應該是一致的。如果寫了一半兒斷電了,那么在File Header中的校驗和就代表著已經修改過的頁,而在File Trailer中的校驗和代表著原先的頁,二者不同則意味著同步中間出了錯。
- 后4個字節代表頁面被最后修改時對應的日志序列位置(LSN)
這個部分也是為了校驗頁的完整性的