[Mysql]InnoDB数据页结构(掘金小册阅读笔记)

1.数据页的基本介绍

数据页的大小是16kb,有很多种数据页。

比如存放表空间头部信息的页,存放Insert Buffer信息的页,存放INODE信息的页,存放undo日志信息的页等。

 

名称中文名占用空间大小简单描述
File Header文件头部38字节页的一些通用信息
Page Header页面头部56字节数据页专有的一些信息
Infimum + Supremum最小记录和最大记录26字节两个虚拟的行记录
User Records用户记录不确定实际存储的行记录内容
Free Space空闲空间不确定页中尚未使用的空间
Page Directory页面目录不确定页中的某些记录的相对位置
File Trailer文件尾部8字节校验页是否完整

我们自己存储的记录会按照我们指定的行格式存储到User Records部分。

我们插入记录会按照指定的行格式来插入进去。刚开始并没有User Records这部分。

每当我们插入一条记录,都会从Free Space部分,也就是尚未使用的存储空间中申请一个记录大小的空间划分到User Records部分,当Free Space部分的空间全部被User Records部分替代掉之后,也就意味着这个页使用完了,如果还有新的记录插入的话,就需要去申请新的页了。

 

如果我们将某一个列指定为主键的话,在具体的行格式中InnoDB就没必要为我们去创建那个所谓的 row_id 隐藏列了。

上一篇笔记中,行格式记录,实际上就是每一条记录都有的信息。

其中记录头信息是有这5个字节,40位的信息。

记录头信息中各个属性的大体意思浏览一下(使用Compact行格式):

名称大小(单位:bit)描述
预留位11没有使用
预留位21没有使用
delete_mask1标记该记录是否被删除
min_rec_mask1B+树的每层非叶子节点中的最小记录都会添加该标记
n_owned4表示当前记录拥有的记录数
heap_no13表示当前记录在记录堆的位置信息
record_type3表示当前记录的类型,0表示普通记录,1表示B+树非叶节点记录,2表示最小记录,3表示最大记录
next_record16表示下一条记录的相对位置

TIPS: 

  • B+树的非叶子节点记录,也是有标识的。
  • 记录中的数据应该是以二进制存在的。
  • 各条记录在User Records中存储的时候并没有空隙。
  • delete_mask 标记着当前记录是否被删除,占用1个二进制位,值为0的时候代表记录并没有被删除,为1的时候代表记录被删除掉了。被删除的记录还在页中!!这些被删除的记录之所以不立即从磁盘上移除,是因为移除它们之后把其他的记录在磁盘上重新排列需要性能消耗,所以只是打一个删除标记而已,所有被删除掉的记录都会组成一个所谓的垃圾链表,在这个链表中的记录占用的空间称之为所谓的可重用空间,之后如果有新记录插入到表中的话,可能把这些被删除的记录占用的存储空间覆盖掉。
  • 这个delete_mask位设置为1和将被删除的记录加入到垃圾链表中其实是两个阶段
  • min_rec_mask  B+树的每层非叶子节点中的最小记录都会添加该标记

  • heap_no

    这个属性表示当前记录在本中的位置,从图中可以看出来,我们插入的4条记录在本中的位置分别是:2345。是不是少了点啥?heap_no值为01的记录,这两个记录并不是我们自己插入的,所以有时候也称为伪记录或者虚拟记录。这两个伪记录一个代表最小记录,一个代表最大记录

  • 记录也可以比大小,对于一条完整的记录来说,比较记录的大小就是比较主键的大小。

两条伪记录分别为最小记录与最大记录(跟图上的Infimum + supremum 有关)。这两条记录的构造十分简单,都是由5字节大小的记录头信息和8字节大小的一个固定的部分组成的,如图所示

它们并不存放在User Records部分,他们被单独放在一个称为Infimum + Supremum的部分

next_record

表示从当前记录的真实数据到下一条记录的真实数据的地址偏移量。

比方说第一条记录的next_record值为32,意味着从第一条记录的真实数据的地址处向后找32个字节便是下一条记录的真实数据。

其实就是个链表。

下一条记录指得并不是按照我们插入顺序的下一条记录,而是按照主键值由小到大的顺序的下一条记录。

所以下一条记录并不一定物理连续,

 Infimum记录(也就是最小记录) 的下一条记录就是本页中主键值最小的用户记录,而本页中主键值最大的用户记录的下一条记录就是 Supremum记录(也就是最大记录)

是一个链表形势的把数据穿插起来。

删除记录的话,就是相当于链表的next record的指针移动(偏移量的变化),实际上物理上面并没有移除。

并且被删除的记录next record为0;

不论我们怎么对页中的记录做增删改操作,InnoDB始终会维护一条记录的单链表,链表中的各个节点是按照主键值由小到大的顺序连接起来的。

next_record这个指针为什么要指向记录头信息和真实数据之间的位置呢?

因为这个位置刚刚好,向左读取就是记录头信息,向右读取就是真实数据。我们前边还说过变长字段长度列表、NULL值列表中的信息都是逆序存放,这样可以使记录中位置靠前的字段和它们对应的字段长度信息在内存中的距离更近,可能会提高高速缓存的命中率。

大喊牛逼,感觉意思就是因为之前的额外信息是逆序存储的,那么从中间进行向左推遍历,就能正序的访问列的变长大小之类的了。而且,这样也比较好的向右寻找到对应真实数据。左右相对对称,

而且,如果删除的记录被标记了删除,存储空间没有回收的话,再次插入主键相同的,会进行复用之前的物理空间

当数据页中存在多条被删除掉的记录时,这些记录的next_record属性将会把这些被删除掉的记录组成一个垃圾链表,以备之后重用这部分存储空间。

2. Page Header(页面头部)

数据页的第二部分!!!占56个字节,存储各种状态信息。

这是为了得到一个数据页中存储的记录的状态信息,比如本页中已经存储了多少条记录,第一条记录的地址是什么,页目录中存储了多少个槽等等,特意在页中定义了一个叫Page Header的部分,它是结构的第二部分,这个部分占用固定的56个字节,专门存储各种状态信息,具体各个字节都是干嘛的看下表:

名称占用空间大小描述
PAGE_N_DIR_SLOTS2字节在页目录中的槽数量
PAGE_HEAP_TOP2字节还未使用的空间最小地址,也就是说从该地址之后就是Free Space
PAGE_N_HEAP2字节本页中的记录的数量(包括最小和最大记录以及标记为删除的记录)
PAGE_FREE2字节第一个已经标记为删除的记录地址(各个已删除的记录通过next_record也会组成一个单链表,这个单链表中的记录可以被重新利用)
PAGE_GARBAGE2字节已删除记录占用的字节数
PAGE_LAST_INSERT2字节最后插入记录的位置
PAGE_DIRECTION2字节记录插入的方向
PAGE_N_DIRECTION2字节一个方向连续插入的记录数量
PAGE_N_RECS2字节该页中记录的数量(不包括最小和最大记录以及被标记为删除的记录)
PAGE_MAX_TRX_ID8字节修改当前页的最大事务ID,该值仅在二级索引中定义
PAGE_LEVEL2字节当前页在B+树中所处的层级
PAGE_INDEX_ID8字节索引ID,表示当前页属于哪个索引
PAGE_BTR_SEG_LEAF10字节B+树叶子段的头部信息,仅在B+树的Root页定义
PAGE_BTR_SEG_TOP10字节B+树非叶子段的头部信息,仅在B+树的Root页定义

这里面比较需要注意的:

PAGE_DIRECTION

假如新插入的一条记录的主键值比上一条记录的主键值大,我们说这条记录的插入方向是右边,反之则是左边。用来表示最后一条记录插入方向的状态就是PAGE_DIRECTION(比主键大不大,决定了插入方向)

PAGE_N_DIRECTION

假设连续几次插入新记录的方向都是一致的,InnoDB会把沿着同一个方向插入记录的条数记下来,这个条数就用PAGE_N_DIRECTION这个状态表示。当然,如果最后一条记录的插入方向改变了的话,这个状态的值会被清零重新统计。

 

3. File Header(文件头部)

刚才的Page Header是只针对数据页才有的各种状态(比如记录多少个数据)

这个文件头是mysql中,各种页的都通用的内容。

页都通用的一些信息,比方说这个页的编号是多少,它的上一个页、下一个页是谁(38个字节)

 

名称占用空间大小描述
FIL_PAGE_SPACE_OR_CHKSUM4字节页的校验和(checksum值)
FIL_PAGE_OFFSET4字节页号
FIL_PAGE_PREV4字节上一个页的页号
FIL_PAGE_NEXT4字节下一个页的页号
FIL_PAGE_LSN8字节页面被最后修改时对应的日志序列位置(英文名是:Log Sequence Number)
FIL_PAGE_TYPE2字节该页的类型
FIL_PAGE_FILE_FLUSH_LSN8字节仅在系统表空间的一个页中定义,代表文件至少被刷新到了对应的LSN值
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID4字节页属于哪个表空间

FIL_PAGE_SPACE_OR_CHKSUM(校验和)

这个代表当前页面的校验和(checksum)。啥是个校验和?就是对于一个很长很长的字节串来说,我们会通过某种算法来计算一个比较短的值来代表这个很长的字节串,这个比较短的值就称为校验和。这样在比较两个很长的字节串之前先比较这两个长字节串的校验和,如果校验和都不一样两个长字节串肯定是不同的,所以省去了直接比较两个比较长的字节串的时间损耗。

FIL_PAGE_OFFSET(唯一编号)

每一个都有一个单独的页号,就跟你的身份证号码一样,InnoDB通过页号来可以唯一定位一个

FIL_PAGE_TYPE

InnoDB为了不同的目的而把页分为不同的类型。

页面类型还有:

类型名称十六进制描述
FIL_PAGE_TYPE_ALLOCATED0x0000最新分配,还没使用
FIL_PAGE_UNDO_LOG0x0002Undo日志页
FIL_PAGE_INODE0x0003段信息节点
FIL_PAGE_IBUF_FREE_LIST0x0004Insert Buffer空闲列表
FIL_PAGE_IBUF_BITMAP0x0005Insert Buffer位图
FIL_PAGE_TYPE_SYS0x0006系统页
FIL_PAGE_TYPE_TRX_SYS0x0007事务系统数据
FIL_PAGE_TYPE_FSP_HDR0x0008表空间头部信息
FIL_PAGE_TYPE_XDES0x0009扩展描述页
FIL_PAGE_TYPE_BLOB0x000A溢出页
FIL_PAGE_INDEX0x45BF索引页,也就是我们所说的数据页

FIL_PAGE_PREVFIL_PAGE_NEXT

上一个页号和下一个页号,类似双向链表,把页穿起来了。不需要物理关联。

并不是所有类型的页都有上一个和下一个页的属性,不过我们本集中唠叨的数据页(也就是类型为FIL_PAGE_INDEX的页)是有这两个属性的,所以所有的数据页其实是一个双链表

 

 

 

 

 

 

 

 

感谢大佬得书!!https://juejin.im/book/5bffcbc9f265da614b11b731/section/5bffdb30518825773a2ed38c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值