08.数据页结构
基础:
- 页是InnoDB管理存储空间的基本单位,一个页的大小一般是
16KB
- 数据页(索引页)是页的一种,用来组织和和存放用户数据
- 索引页之间通过双向链表连接,数据页的物理结构不连续
数据页的详细结构:
数据页组成: |名称|中文名|占用空间大小|简单描述|
|:—-|:—-|:—-|:—-|
|
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构成双向循环列表| |FIL_PAGE_NEXT|4字节|下一个页的页号,与FIL_PAGE_PREV构成双向循环列表| |FIL_PAGE_LSN|8字节|页面被最后修改时对应的日志序列位置(英文名是:Log Sequence Number),用来做数据恢复时判断该页是否已经持久化| |FIL_PAGE_TYPE
|2字节|该页的类型| |FIL_PAGE_FILE_FLUSH_LSN|8字节|仅在系统表空间的一个页中定义,代表文件至少被刷新到了对应的LSN值| |FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID|4字节|页属于哪个表空间|FIL_PAGE_TYPE
(页类型): |FIL_PAGE_TYPE
类型名称|十六进制|描述| |:—-|:—-|:—-| |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|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|0x000A|BLOB页,溢出页| |FIL_PAGE_INDEX|0x45BF|索引页,也就是我们所说的数据页|页之间的连接:FIL_PAGE_PREV + FIL_PAGE_NEXT 构成双向循环链表,不是所有类型的页都有这个
采用链表的结构是让数据页之间不需要是物理上的连续的,但逻辑上可以保持连续
Page Header
(页面头部):
Page Header 组成 |
空间大小 | 描述 |
---|---|---|
PAGE_N_DIR_SLOTS | 2字节 | 在页目录中的槽数量 |
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+树的Root页定义 |
PAGE_BTR_SEG_TOP | 10字节 | B+树非叶子段的头部信息,仅在B+树的Root页定义 |
Infimum + Supremum
:
- 作用:方便遍历
- mysql 自己创建的两条伪记录
- 由于这两条记录不是我们自己定义的记录,所以它们并不存放在页的
User Records
部分,他们被单独放在一个称为Infimum + Supremum的部分 - 本页中Infimum记录(也就是最小记录)的下一条记录就是本页中主键值最小的用户记录
- 本页中主键值最大的用户记录的下一条记录就是 Supremum记录(也就是最大记录)
user_records
:
- 作用:存放用户记录
- next_record:
- 记录之间通过next_record(偏移量)连接,按照主键的大小顺序排成一个单向链表(黑色箭头)
- next_record记录的是从
当前记录的真实数据处
到下一条记录的真实数据的地址
偏移量(字节)(不是下一条记录的头部) - 为什么要指向记录头信息和真实数据之间的位置:这个位置刚刚好,向左读取就是记录头信息,向右读取就是真实数据
- 本页中Infimum记录(也就是最小记录)的下一条记录就是本页中主键值最小的用户记录
- 本页中主键值最大的用户记录的下一条记录就是
Supremum记录(也就是最大记录)
- 删除和重用空间
- 删除的记录,会通过next_record将会把这些被删除掉的记录组成一个
垃圾链表
,以备之后重用 - 删除一条记录:
- 记录并没有从存储空间中直接移除
- 把该条记录的delete_mask值设置为1
- next_record值变为了0
- 修改前一个记录的next_record指向
- 最大记录Supremum的n_owned值减1
- 恢复一条记录:
- 直接复用了原来被删除记录的存储空间
- 删除的记录,会通过next_record将会把这些被删除掉的记录组成一个
Page Directory
(页目录):
- 作用:通过页目录中槽内的值,可以在页内快速找到目标记录所在的分组,然后从对应分组内最小记录遍历即可(这样就不需要遍历页内的所有记录)
- 页目录由槽组成
- 页目录的槽存储的是每个分组中最后一条记录的偏移位置
- 槽中的数据是顺序排放的
- 页目录的分组规则:
- 最小记录(infimun)独立为1组
- 最大记录(supremum)所在的分组拥有 1~8 条记录
- 剩下的若干个分组每组可以有 4~8 条记录
- 初始时有两个槽,即最小记录的槽,最大记录的槽。插入数据后,会放入最大记录的槽
- 当槽内的记录超过8个,该组会发生分裂,变成两个分组,用两个槽来记录
- 每组记录用一个槽位记录,组内最大记录的user_records起始位置的偏移量(蓝色箭头)
- 最大记录的n_owned记录当前组内的记录数
- 在数据页内查找数据的过程:
- 通过
二分法
确定该记录所在的槽 - 找到该槽中主键值最小的那条记录
- 通过next_record属性
遍历
该槽所在的组中的记录找到符合的记录
- 通过
File Trailer
:
- 作用:解决刷盘过程中断电导致的数据不完整
- 具体原理:
- 每当一个页面在内存中修改了,在同步之前就要把它的校验和算出来,因为
File Header
在页面的前面,所以校验和会被首先同步到磁盘,当完全写完时,校验和也会被写到页的尾部,如果完全同步成功,则页的首部和尾部的校验和应该是一致的。如果写了一半儿断电了,那么在File Header
中的校验和就代表着已经修改过的页,而在File Trialer
中的校验和代表着原先的页,二者不同则意味着同步中间出了错
- 每当一个页面在内存中修改了,在同步之前就要把它的校验和算出来,因为
本文作者:navyum
本文链接:https://www.cnblogs.com/navyum/p/18509411
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步