一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情
探秘InnoDB数据页(一)
-
页是
InnoDB
管理存储空间的基本单位,一个页的大小一般是16KB
。 -
InnoDB
设计了多种不同类型的页,以管理不同的数据- 存放表空间头部信息的页
- 存储
Change Buffer
信息的页 - 存放
INODE
信息的页 - 存放
undo
日志信息的页 - 存放记录的页(索引页)
数据页结构
我们先来一张InnoDB的数据页由哪几个部分组成
具体描述如下表格所示:
名称 | 大小(比特) | 描述 |
---|---|---|
预留位1 | 1 | 没有使用 |
预留位2 | 1 | 没有使用 |
deleted_flag | 1 | 标记该记录是否被删除 |
min_rec_flag | 1 | B+树种每层非叶子节点中的最小的目录项记录都会添加该标记 |
n_owned | 4 | 一个页面中的记录会被分成若干个组,每个组中有一个记录是“带头大哥”,其余的记录都是“小弟”。“带头大哥”记录的n_owned 值代表该组中所有的记录条数,“小弟”记录的n_owned 值都为0 |
heap_no | 13 | 表币当丽记录在页面堆中的相对位置 |
record_type | 3 | 表示当前记录类型 0 表示普通记录表示 1 B+ 树非叶节点的目录项记录 2 表示 Infimum 记录 3 表示 Supremum 记录 |
next_record | 16 | 表示下一条记录的相对位置 |
Infimum 与 Supremum 记录
为什么会存在
Infimum
与Supremum
这两个虚拟记录呢?
我们关注记录头信息中,有min_rec_flag
与n_owned
,前者为最小记录时添加标记,后者为一个组最大的记录使用,而一个页中,Supremum
必然为其中一个老大,因此直接规定最小及最大记录,可以在创建数据页时,给以一定的便利。
另外,相信我们都写过链表相关的算法题,一般我们都会在链表相关的算法题中,给链表添加一个虚拟头节点,以简化我们对边界条件的讨论,我个人认为,InnoDB
在设计的时候,一定程度上也是借用了这样的思想。
其他头记录讨论
deleted_flag
:该记录逻辑删除的标志位
为什么要有这样一个deleted_flag的标志位?
InnoDB
搞这个标志位说明当我们操作数据删除记录时,记录不会真的被实时删除,取而代之的是打一个逻辑删除的标志。
这样子做的好处在于:
- 避免重新排列数据页带来的性能消耗
因为在删除时只是打了个标记,没有真的进行删除,所以这个时候会产生磁盘碎片,为了重新利用这些空间,InnoDB
会将被删除掉的记录会组成一个垃圾链表,记录在这个链表中占用的空间称为可重用空间。
heap_no
:表示每条记录的序号
作用:可以使用heap_no来比较记录大小
为了让Infimum
与Supremum
完整的表示最小节点最最大节点的概念,Infimum
的序号为1,Supremum
的序号为0.
next_record
:表示从当前记录的真实数据到下一条记录的真实数据的距离
next_record这个指针为什么要指向记录头信息和真实数据之间的位置?
因为这个位置向左可读头记录信息,向右可读真实数据,这也是头记录信息中,变长字段列表、NULL值列表中的信息都是逆序存放的原因。
我想,InnoDB
这么设计的原因在于,使记录中位置靠前的字段和它们对应的字段长度信息在内存中的距离更近,这可能会提高高速缓存的命中率。
怎么理解提高高速缓存的命中率这个说法呢?
对于CPU
来说,它是不会低效的一个个字节的加载的,而是一块一块的加载数据,而一个Cache Line
可能为64Bytes,也就说,当next_record
记录的位置使头记录与真实数据靠的更近了,CPU
完全有可能直接命中缓存而减少加载,这就是InnoDB
这么设计的意义。