共计 3204 个字符,预计需要花费 9 分钟才能阅读完成。
前言
大家好,我是 xicheng。最近天气变凉,留神身材。当初持续更新 MySQL 的 InnoDB 的相干文章,InnoDB 的常识脑图如下所示,大家坐稳了。
InnoDB 页简介
默认是 16KB。大小只能在第一次初始化 MySQL 数据目录时指定。是 InnoDB 用于存放数据与索引的页。
InnoDB 数据页大体构造
名称 | 中文名 | 大小(字节) | 简略形容 |
---|---|---|---|
File Header | 文件头 | 38 | 页的一些通用信息 |
Page Header | 页头 | 56 | 数据页专有的一些信息 |
Infimum + SupreMum | 最小记录和最大记录 | 26 | 两个虚构的行记录 |
User Records | 用户记录 | 不确定 | 理论存储的行记录内容 |
Free Space | 闲暇空间 | 不确定 | 页中尚未应用的空间 |
Page Directory | 页目录 | 不确定 | 页中某些记录的绝对地位 |
File Trailer | 文件尾部 | 8 | 测验页是否残缺 |
如下图所示
File Header
用来记录页的一些头信息。针对各种类型的页都通用 File Header 属性如下表所示。* 为重点把握的属性。
名称 | 占用空间(字节) | 形容 |
---|---|---|
*FIL_PAGE_SPACE_OR_CHKSUM | 4 | ⻚的校验和(checksum 值) |
*FIL_PAGE_OFFSET | 4 | ⻚号 |
*FIL_PAGE_PREV | 4 | 上⼀个⻚的⻚号 |
*FIL_PAGE_NEXT | 4 | 下⼀个⻚的⻚号。通过该属性与 FIL_PAGE_PREV 属性,实现了 B + 树中,叶子结点是由双向链表形成,能疾速遍历的个性。 |
FIL_PAGE_LSN | 8 | ⻚⾯被最初批改时对应的⽇志序列地位 |
*FIL_PAGE_TYPE | 2 | 该⻚的类型。具体页类型在下表中展现 |
FIL_PAGE_FILE_FLUSH_LSN | 8 | 仅在零碎表空间的⼀个⻚中定义,代表⽂件⾄少被刷新到了对应的 LSN 值 |
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID | 4 | ⻚属于哪个表空间 |
页类型,* 为重点把握的页类型。
类型名称 | 十六进制 | 形容 |
---|---|---|
FIL_PAGE_TYPE_ALLOCATED | 0x0000 | 最新调配,还没使⽤ |
*FIL_PAGE_UNDO_LOG | 0x0002 | Undo ⽇志⻚ |
*FIL_PAGE_INODE | 0x0003 | 段信息节点 |
*FIL_PAGE_IBUF_FREE_LIST | 0x0004 | Insert Buffer 闲暇列表 |
FIL_PAGE_IBUF_BITMAP | 0x0005 | Insert 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 | 0x000A | BLOB ⻚ |
*FIL_PAGE_INDEX | 0x45BF | 索引⻚,也就是咱们所说的数据⻚ |
Page Header
上文列举出了很多种类型的页。其中数据页的属性如下表所示。
状态名称 | 大小(字节) | 形容 |
---|---|---|
PAGE_N_DIR_SLOTS | 2 | 在⻚⽬录中的槽数量,在 Page Directory 中会讲到 |
PAGE_HEAP_TOP | 2 | 还未使⽤的空间最⼩地址,也就是说从该地址之后就是 Free Space |
PAGE_N_HEAP | 2 | 本⻚中的记录的数量(包含最⼩和最⼤记录以及标记为删除的记录) |
PAGE_FREE | 2 | 第⼀个曾经标记为删除的记录地址(各个已删除的记录通过 next_record 也会组成⼀个单链表,这个单链表中的记录能够被从新利⽤) |
PAGE_GARBAGE | 2 | 已删除记录占⽤的字节数 |
PAGE_LAST_INSERT | 2 | 最初插⼊记录的地位 |
PAGE_DIRECTION | 2 | 记录插⼊的⽅向 |
PAGE_N_DIRECTION | 2 | ⼀个⽅向间断插⼊的记录数量 |
PAGE_N_RECS | 2 | 该⻚中记录的数量(不包含最⼩和最⼤记录以及被标记为删除的记录) |
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 + 树的根⻚面定义 |
Infimum Record 和 Supremum Record
每个数据页都有两个虚构的行记录,用来限定记录的边界。Infimum 比该页的任何主键值都小。Supremum 比该页的任何主键值都大。如下图所示。
User Record 和 Free Space
User Record 是理论存储行记录的内容。
Free Space 是闲暇空间,是个链表数据结构。当一条记录被删除后,该空间会被退出到闲暇链表中。
Page Directory
数据页的 Page Directory 用于在页内疾速查找某条记录。
分组流程
- 初始状况下⼀个数据⻚⾥只有最⼩记录和最⼤记录两条记录,它们分属于两个分组。
- 最⼩记录所在的分组只能有 1 条记录,最⼤记录所在的分组领有的记录条数只能在 1 - 8 条,剩下的分组中记录的条数范畴只能在是 4 - 8 条之间。
- 将每个组的最初⼀条记录的地址偏移量依照从小到大的顺序存储到 Page Directory 里。Page Directory 的这些地址偏移量被称为槽。
- 之后每插⼊⼀条记录,都会从⻚⽬录中找到主键值⽐本记录的主键值⼤并且差值最⼩的槽,而后把该槽对应的记录的 n_owned 值 +1,示意本组内⼜增加了⼀条记录,直到该组中的记录数等于 8 个为止。
- 在⼀个组中的记录数等于 8 个后再插⼊⼀条记录时,会将组中的记录拆分成两个组,⼀个组中 4 条记录,另⼀个 5 条记录。这个过程会更新以后组对应的槽,且另外会新增⼀个槽来记录这个新增分组中最⼤的那条记录的偏移量。
示例
- 初始状况如下图所示,下图中行记录中属性的含意参见“InnoDB 与其它存储引擎 –InnoDB 的行 –COMPACT”。该页有 2 个组。第 1 组,也就是 Infimum Record 所在的组只有 1 条记录。第 2 组,也就是 Supreme Record 所在的组有 7 条记录。
- 插入 1 条主键值为 2 的记录:槽 1 所指的记录的主键值比待插入记录大,且差值最小。将该组的主键最大记录的 n_owned +1。也就是将 Supreme Record 记录的 n_owned +1。而后调整 next record 指针,调整 heap no。后果如下图所示。
- 插入 1 条主键值为 3 的记录。槽 1 所指的记录的主键值比待插入记录大,且差值最小。此时,槽 1 对应的组曾经有 8 条记录了。则将该组拆分为 2 组。更新这 2 组的 heap_no,next_record,槽等信息。进一步简化后的示意图如下所示。
⻚中查找指定主键值的记录流程
- 通过⼆分法比拟槽所指记录的主键值与待查键值的大小,来确定待查记录所在的槽。
- 确定槽后,从槽所指的记录开始,通过记录的 next_record 属性遍历该槽所在的组中的各个记录,至到找到指定主键的记录,或者遍历残缺个组为止。
File Trailer
所有页类型的 File Trailer 都雷同,一共有 8 个字节,分成 2 个局部。
前 4 个字节代表⻚的校验和(checksum),这个局部和 FileHeader 中 FIL_PAGE_SPACE_OR_CHKSUM 绝对应的。具体通过 InnoDB 的 checksum 函数来运算两者,将运算后果进行比拟。如果后果雷同,才代表页面被残缺的刷新到了磁盘。(因为刷盘是先刷 File Header,后刷 File Trailer)
后 4 个字节与 File Header 中的 FIL_PAGE_LSN 雷同,这个局部也是为了校验⻚的完整性的。
结尾
MySQL 数据页就讲完了,心愿大家能继续关注上来。下一篇 MySQL 文章讲 InnoDB- 表空间。
微信扫描下方二维码,或搜寻“xicheng”,关注公众号后回复【笔记】,有我筹备的 15 万字 Java 面试笔记。
感激各位人才的点赞、珍藏和评论,干货文章继续更新中,下篇文章再见!