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)
这个部分也是为了校验页的完整性的