本文参考自 https://blog.csdn.net/bohu83/article/details/81122829 感谢作者的创作
先看一张图,这张图是一张简图:
再看一张详细的图:
接下来我们解释下具体的结构:
1 File Header 主要是记录该页在整个数据空间的页号,以及页和页之前的指针,有一个属性特别重要
Macro | bytes | Desc |
---|---|---|
FIL_PAGE_SPACE_OR_CHKSUM | 4 | 在MySQL4.0之前存储space id,之后的版本用于存储checksum |
FIL_PAGE_OFFSET | 4 | 当前页的page no,每个表空间从0开始,即这个值乘以数据页的大小就可以得到数据页在文件中的起始偏移量。fio_io 函数读取以及写入数据页的时候依赖这个规则。 |
FIL_PAGE_PREV | 4 | 通常用于维护btree同一level的双向链表,指向链表的前一个page,没有的话则值为FIL_NULL |
FIL_PAGE_NEXT | 4 | 和FIL_PAGE_PREV类似,记录链表的下一个Page的Page No |
FIL_PAGE_LSN | 8 | 最近一次修改该page的LSN:这个字段非常重要,InnoDB redolog幂等的特性就依赖此字段。在奔溃恢复应用日志阶段,如果发现redolog的lsn小于等于这个值,就不需要再次应用redolog了。 |
FIL_PAGE_TYPE | 2 | Page类型 |
FIL_PAGE_FILE_FLUSH_LSN | 8 | 只用于系统表空间的第一个Page,记录在正常shutdown时安全checkpoint到的点,对于用户表空间,这个字段通常是空闲的,但在5.7里,FIL_PAGE_COMPRESSED类型的数据页则另有用途。 |
FIL_PAGE_SPACE_ID | 4 | 存储page所在的space id |
2 Page Header
Macro | bytes | Desc |
---|---|---|
PAGE_N_DIR_SLOTS | 2 | Page directory中的slot个数 (见下文关于Page directory的描述)一个新建的空数据页,就有2个目录,分别指向最大记录和最小记录。 |
PAGE_HEAP_TOP | 2 | 指向当前Page内已使用的空间的末尾便宜位置,即free space的开始位置(空闲空间的起始地址)。大于这个地址的且小于数据目录的空间都是未分配的,可以被后续使用。 |
PAGE_N_HEAP | 2 | Page内所有记录个数,包含用户记录,系统记录以及标记删除的记录,在创建新的空页时候,默认被置为2,即最大和最小记录。此外当第一个bit(最高位)设置为1时,表示这个page内是以新格式Compact格式存储的 |
PAGE_FREE | 2 | 指向标记删除的记录链表的第一个记录 |
PAGE_GARBAGE | 2 | 被删除的记录链表上占用的总的字节数,属于可回收的垃圾碎片空间 |
PAGE_LAST_INSERT | 2 | 指向最近一次插入的记录偏移量,主要用于优化顺序插入操作 |
PAGE_DIRECTION | 2 | 用于指示当前记录的插入顺序以及是否正在进行顺序插入,每次插入时,PAGE_LAST_INSERT会和当前记录进行比较,以确认插入方向,据此进行插入优化 |
PAGE_N_DIRECTION | 2 | 当前以相同方向的顺序插入记录个数 |
PAGE_N_RECS | 2 | Page上有效的未被标记删除的用户记录个数 |
PAGE_MAX_TRX_ID | 8 | 最近一次修改该page记录的事务ID,主要用于辅助判断二级索引记录的可见性。 |
PAGE_LEVEL | 2 | 该Page所在的btree level,根节点的level最大,叶子节点的level为0 |
PAGE_INDEX_ID | 8 | 该Page归属的索引ID |
两个最重要和跟日常操作相关的两个参数,用红色标记出来了。未经分配的空间是通过PAGE_HEAP_TOP管理,已经删除并PURGE的记录通过PAGE_FREE管理
3 最大最小记录
最大记录是这个数据页中逻辑上最大的记录,所有用户的记录都小于它。最小记录是数据页上最小的记录,所有用户记录都大于它。他们在数据页被创建的时候创建,而且不能被删除。是固定大小(13)固定位置的, 引入他们主要是方便页内操作。
我们知道索引的内节点中,每一层最左侧的页面的记录,就是起索引作用的。它指向孩子节点中的数据,在索引本身的排序属性下,比本节点的数据都小。INNODB中就是使用最小记录来承担这个责任。
还有一个作用就是在遍历数据的时候,通过槽slot从前往后找到数据的过程中,只要找到最小或者最大记录,就意味着已经到了本页面的边界,数据已经遍历完了。
4 数据目录(Page Directory)
INNODB引入了slot槽的概念,槽的作用就是页面内搜索数据的,可以理解为在页内构建的一个很小的索引(sparse index)来辅助二分查找。为了管理记录,把多条数据对应一个槽,或者说一个槽own多条记录。到底是几条呢?书上没有给出很细的解释,每个槽占用两个字节(PAGE_DIR_SLOT_SIZE),存储对应记录的页内偏移量。在槽指向的记录中,会有字段记录own记录的数量。由此可见,槽own的记录不能太多,因为太多的话,即意味着槽太过稀疏,不能很好的提高查询效率,但同时也不能own太少,这会导致槽数量变多,占用过多的空间。在InnoDB的实现中,槽own的记录数量在(PAGE_DIR_SLOT_MIN_N_OWNED-PAGE_DIR_SLOT_MAX_N_OWNED)4-8之间,包括4和8,平均是6个记录。如果超过这个数量,就需要重新均衡槽的数量。槽的增加和删除可能需要进行内存拷贝,但是由于槽占用的总体空间很小,开销可以忽略不计。
注意,数据目录里记录的是该页内的记录的偏移量,也就是数值型的一个值,而记录之间有指针相连。
5 空闲空间(Free Space)
从PAGE_HEAP_TOP开始,到最后一个数据目录,这之间的空间就是空闲空间,都被重置为0,插入数据的过程中,如果需要插入页面,系统就会从这个页面的heap申请所需要的空间。
虽说页中把空闲空间画出来了,但是其实PAGE_HEAP_TOP是在页头部里的
这是空间的分配,如果记录被删除并purge了。则系统会把这个记录对应的空间,通过page free管理。每次在页面删除记录后,都会把新删除记录对应空间的NEXT指向原来PAGE_FREE指向的空间,然后再将PAGE_FREE指向新删除的记录空间的首地址,这样就通过链表管理起来了。因为在页面记录中,都会在记录首地址的前两个字节位置存储当前记录的下一个记录,用来将记录之间形成一个单项列表,那么自然被删除的空间也可以通过这个指针串联起来,最终通过PAGE_FREE管理起来。
现在知道未经分配的空间是通过PAGE_HEAP_TOP管理,已经删除并PURGE的记录通过PAGE_FREE管理
6 Fil Trailer
没什么需要特别注意的