- 数据页页结构总览
- 记录在页中怎么存储的
- 记录的头信息
- deleted_flag
- min_rec_flag
- n_owned
- heap_no
- record_type
- next_record
- Page Directory
- Page Header
- File Header
- File Tailer
Innodb设计了不同类型的页结构,存放表空间头部信息的页、存放change buffer信息的页、存放inode信息的页、存放undo redo日志信息的页;今天我们了解存放记录信息的页(数据页也叫索引页)
数据页页结构总览
名称 | 描述 |
filer header | 页的一些通用信息 |
page header | 数据页专有的一些信息 |
infimum + supremum | 两个虚拟的记录 |
user records | 用户存储的记录内容 |
free space | 页中尚未使用的空间 |
page directory | 页中某些记录的相对位置 |
file tailer | 校验也是否完整 |
记录在页中怎么存储的
记录的头信息
上节已经介绍过记录的头信息结构了,我们重点介绍一些各个结构的具体作用
deleted_flag
主要用来标示当前记录是否被删除。占用一个bit。0-代表未删除;1-代表已删除。
要注意的一点是,打上标记的记录并不会真正被删除,而是加入到垃圾链表,等到下次有相同位置的数据插入就可以复用,而且还能避免删除记录造成的数据页重新排列(消耗性能)
min_rec_flag
B+数每层非叶子结点中最小的目录项记录都会添加该标示
n_owned
一个页面中的记录会被分成很多组(利用分组二分法加快页中的记录查找,不用从第一条遍历到最后一条来查找),每个组中最后一个记录的n_owned代表组中的记录条数,其余的记录n_owned值都为0;记录是怎么样的呢,看下图
一个数据页16k,可以存储很多条记录,如果通过索引定位到了记录在某一页,然后遍历整个页面来查找记录,复杂度就太高了。所以设计了分组的概念,这样就可以通过page directory中记录的槽信息利用二分法来查找了:
- 初始条件下只有两个记录infimum 和 supremum,分别处于两个组,page directory就是记录这些组中的记录偏移量的。
- 当一个组中的记录超过8条后,就会拆分两个组,原来的组变成4个,新增的组变成5个,也会在页目录中新增一个槽,记录这个新增组中最大那条记录的偏移量。
heap_no
标示当前记录在页面堆中的相对位置
record_type
记录类型 0-普通记录 1-B+数非叶子结点的记录 2-表示Infimum记录 3-表示Superemum记录
next_record
下一条记录的相对位置,指向的如上图中是头信息和实际数据之间的位置,这就是记录存储结构设计的巧妙之处了,往左可以读取到记录的头信息,变长字段长度信息,null值列表信息,往右是真实数据。前面还说过变长字段长度信息,null值列表信息这些都是逆序存储的。这样记录中位置靠前的字段和他们的长度信息距离更新,可以提高cpu高速缓存的命中率。
Page Directory
如上图记录存放分组的槽,用来二分法快速查找数据页中的记录
Page Header
类似于继承关系的抽象file header和file tailer是每个数据页都有的结构,定义了一些通用结构,而page header是不同的数据页有自己不同的实现
名称 | 大小(字节) | 描述 |
PAGE_N_DIR_SLOTS | 2 | 页目录中的槽数量 |
PAGE_HEAP_TOP | 2 | 还未使用的空间最小地址,该地址之后就是free space |
PAGE_N_HEAP | 2 | 第一位表示是否是紧凑型记录,剩余15位表示本页的堆中记录的数据(包含infimum、supremum以及标记为已删除的记录) |
PAGE_FREE | 2 | 已删除的记录会通过next_record组成一个单向链表,可以被重新利用 |
PAGE_GARBAGE | 2 | 已删除记录占用的字节数 |
PAGE_LAST_INSERT | 2 | 最后插入记录的位置 |
PAGE_DIRECTORY | 2 | 记录插入的方向 |
PAGE_N_DIRECTORY | 2 | 一个方向连续插入记录的数量 |
PAGE_N_RECS | 2 | 该页面中包含的记录的数量(不包含infimum、supremum以及标记为已删除的记录) |
PAGE_MAX_TRX_ID | 8 | 修改当前页的最大事务id,仅在二级索引页面中定义 |
PAGE_LEVEL | 2 | 当前页在B+树中所处的层级 |
PAGE_INDEX_ID | 8 | 索引id表示当前页属于哪个索引 |
PAGE_BTR_SEG_LEAF | 10 | B+树叶子节点段的头部信息,仅在B+树的根页面中定义 |
PAGE_BTR_SEG_TOP | 10 | B+树非叶子节点段的头部信息,仅在B+树的根页面中定义 |
File Header
多个页之间是通过双向链表相连的,这样也利于查找的时候向前和向后进行
名称 | 大小(字节) | 描述 |
FIL_PAGE_SPACE_OR_CHKSUM | 4 | 页面的校验和 |
FIL_PAGE_OFFSET | 4 | 页号 |
FIL_PAGE_PREV | 4 | 上一页的页号 |
FIL_PAGE_NEXT | 4 | 下一页的页号 |
FIL_PAGE_LSN | 8 | 页面被修改后的LSN值 |
FIL_PAGE_TYPE | 2 | 该页的类型 |
FIL_PAGE_FILE_FLUSH_LSN | 8 | 仅在表空间的第一个页中定义,代表文件至少被刷新到了对应的LSN值(在msyql崩溃恢复的时候就是读取这个LSN来进行redolog的恢复) |
FIL_PAGE_ARCH_LOG_NO_OR_SPACEID | 4 | 该页属于哪个表空间 |
其他页的类型:
名称 | 十六进制 | 描述 |
FIL_PAGE_TYPE_ALLOCATED | 0x0000 | 最新分配,还未使用 |
FIL_PAGE_UNDO_LOG | 0x0002 | undo日志页 |
FIL_PAGE_INODE | 0x0003 | 存储段的信息 |
FIL_PAGE_IBUF_FREE_LIST | 0x0004 | change buffer空闲列表 |
FIL_PAGE_IBUF_BITMAP | 0x0005 | change buffer的一些属性 |
FIL_PAGE_TYPE_SYS | 0x0006 | 存储一些系统数据 |
FIL_PAGE_TYPE_TRX_SYS | 0x0007 | 事务系统数据 |
FIL_PAGE_TYPE_FSP_HDR | 0x0008 | 表空间头部信息 |
FIL_PAGE_TYPE_XDES | 0x0009 | 存储区的一些信息 |
FIL_PAGE_TYPE_BLOB | 0x0000A | 溢出页 |
FIL_PAGE_INDEX | 0x45BF | 索引页(数据页) |
File Tailer
8个字节组成,前4个字节代表页的校验和,每一次的页的信息被刷新到磁盘之前,都会被页的校验和算出来写入到file header的check sum中,完成之后会再写入到file tailer的check sum字段,二者一致代表刷新成功,反正刷新期发生了错误
后4个字节代表代表修改后的LSN,正常情况下应该和header中的FIL_PAGE_LSN相同。这两个字段都是为了校验页面的完整性。