前面介绍了页的基本信息,mysql为了不同的目的设计了多种不同类型的页,比如存放undo日志的页,存放INODE信息的页等,但是我们更关心存放表记录的页,官方叫索引页(Index),也就是今天的主题,我们还没有介绍过索引,所以为了不让大家引起迷惑,暂时叫数据页吧。
数据结构
页由以上7个部分组成,讲解的顺序由浅入深,不会按照数据存储的顺序来讲述。
数据结构页中记录存储
其中 infimum supremum 与user records 这些是记录。刚开始生成页的时候,没有user records,每插入一条记录,都会从free space 中申请一个记录大小的内存空间,当free space使用完后,这个页也就使用完了。那么页中数据怎么管理的呢?我们以COMPACT行存储方式举例。
记录头
上次我们简单介绍记录头中的信息,这次我们的详细的聊聊。
名称 | 大小(byte) | 描述 |
---|---|---|
预留位1 | 1 | 没使用 |
预留位2 | 1 | 没使用 |
delete_flag | 1 | 0未删除1删除 |
min_rec_flag | 1 | B 树每层非叶子节点最小的目录项会添加标记 |
n_owned | 4 | 页中的记录都会分为不同的组,其中有个记录是带头大哥,其余的是小弟,带头大哥记录的是组内的条数,小弟记录的是0 |
heap_no | 13 | 记录在页面堆中的相对位置 |
record_type | 3 | 表示当前记录类型,0普通记录,1B 树非页节点的目录项纪录,2表示Infimum,3表示Supremum |
next_record | 16 | 下一条记录真实数据的相对位置 |
delete_flag
看到这里大家是否有疑问,为啥记录头会有这个属性。原因是因为如果直接物理删除掉后,需要在磁盘重新排序其他记录,会造成性能消耗,所以打个标记来避免。所有被删除的记录会形成一个垃圾链表,记录的空间叫可重用空间,如果有新记录插入,直接复用被删除记录的空间。(至于何时刷新,后续后讲解)
min_rec_flag
B 树每层非叶子节点最小的目录项会添加标记。(聊索引时会重点说)
n_owned
这个暂时保密,稍后它是主角。
heap_no
每一条记录亲密无间的排列的结构叫堆,从第3条用户记录开始根据主键排列,heap_no就是每个记录的在堆里的相对位置。细心的观众会发现为啥从第三条记录开始,是因为每个页都添加了Infimum与Supremum这两条记录。
- Infimum
存储的内容是固定的单词Infimum由16进制表示。第一条记录,
并且是页面最小的记录。
- Supremum
存储的内容是固定的单词Supremum由16进制表示。第二条记录,并且是页面最大的记录。
record_type
后续聊到索引的时候我们重点讲。
next_record
表示当前记录的真实数据到下一条真实数据的地址距离。正数在当前地址后面;负数则代表在当前地址前面。有个重点哈,下一条记录不是插入顺序而是主键排序顺序。
- 删除 细心的会发现,这就是个单向链表,那么删除操作我就不多说了。
- 新增 会在已删除的空间上直接复用,进行节点添加。
为什么next_record是表示当前记录的真实数据到下一条真实数据的地址距离呢?之前我们聊过行存储,如果还记得,那就非常好理解了,此处重点指针在这,向左是记录头信息,向右是真实数据,而且变成字段列表和null值列表都是字段逆序存放,这样在查询时所需要的信息离在内存中距离更近,提高高速缓存的命中率。
考虑到一口吃不成个胖子,所以余下的后续详细讲述。