InnoDB记录的存储结构与数据页结构

227 阅读5分钟

记录的存储结构

作为磁盘和内存之间的交互单位,页的大小一般为16KB。在一般情况下,一次最少从磁盘中读取16KB的内容到内存,一次最少把内存的16KB刷新到磁盘

InnoDB行格式

InnoDB行格式有4种,分别是COMPACT、REDUNDANT、DYNAMIC、COMPRESSED。其中,DYNAMIC行格式是MySQL 5.7 / 5.8版本默认的

COMPACT行格式

image.png

记录的额外信息
  • 变成字段列表:记录字段真实数据所占用的字节数。变长字段,如TEXT、VARCHAR等
  • NULL值列表:每个允许存NULL的列对应一个二进制位
  • 记录头信息image.png

image.png

名称大小描述
deleted_flag1标记该记录是否被删除
min_rec_flag1B+树的每层非叶子节点中最小目录项记录
n_owned4当前记录拥有的记录数(分组后带头大哥所代表记录总数,小弟为0)
heap_no13当前记录在页面堆中的位置(记录紧密排列的结构称为)
record_type3当前记录类型:0表示普通记录 1表示B+树非叶子节点目录项记录 2表示Infimum记录 3表示Supremum记录
next_record16下一条记录的相对位置

被删除的记录还在页中吗?为什么不直接删除?
被删除的记录还在页中,还在真实的磁盘上,只是将记录的行格式上的头信息的删除标记位deleted_flag设置为1。如果直接移除它们,需要在磁盘上重新排列其他记录,带来性能损耗。被删除的记录会组成一个垃圾链表,垃圾链表占用的空间是可以重用的,如果新的记录插入到表中,可以直接覆盖垃圾链表占用的空间。

堆中记录的heap_no值在分配后就不会发生改变。

Infimum记录的下一条记录就是本页中主键值最小的用户记录,本页中主键值最大的用户记录的下一条记录就是Supremum记录。规定Infimum记录为页面中最小记录,Supremum记录为页面中最大的记录

记录在页中的示意图

image.png

记录的真实数据

InnoDB存储引擎会为每条记录都添加 transaction_id 和 roll_pointer 这两个列,但是 row_id 是可选的(在没有自定义主键以及Unique键的情况下才会添加该列)

名称大小占用空间描述
row_id6字节行ID,唯一标识一条记录
transaction_id6字节事务ID
roll_pointer7字节回滚指针

溢出列(针对DYNAMIC行格式)

一条记录的某个列中存储的数据占用字节数非常多时,会导致溢出列。溢出列的所有数据都存储在溢出页中,行格式的记录的真实数据某个字段只存储指向溢出页的地址

image.png

数据页结构

常用的页面类型

  • 存放表中记录的页,称为数据页/索引页
  • 存放表空间头部信息的页
  • 存放Change Buffer的页
  • 存放INODE信息的页
  • 存放undo日志信息的页

数据页的结构

16KB的数据页可以划分为以下几个部分

image.png

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

1. 记录在数据页中的存储

表中的记录会按照指定的行格式存储在User Records部分。每当插入一条记录时,都会从Free Space申请一记录大小的空间。如果该数据页的剩余空间使用完,就去申请新的页。记录在页中按主键值从小到大的顺序组成一个单项链表

image.png

2. Page Directory(页目录)

规定:Infimum记录所在组只能有1条记录,Supremum记录所在组所有拥有的记录条数只能在1-8之间,剩下的分组中记录的条数只能在4-8之间。 槽存放每组中最大的那条记录在页面中的地址偏移量,通过槽可以快速定位对应记录的主键值

一个数据页中根据主键值查找指定记录时的过程如下:

  • 通过二分法确定该记录所在分组对应的槽,找到该槽所在分组中主键值最小的那条记录
  • 通过记录的next_record属性遍历该槽所在的组中的各个记录

查找主键值为6的记录过程,自己回忆

image.png

3. Page Header页面头部

  • 数据页存储多少条记录
  • 页目录中槽的数量

4. File Header文件头部

页的第一组成部分

  • 页的校验和
  • 页号
  • 上一个页的页号
  • 下一个页的页号

5. File Trailer文件尾部

  • 检测一个页是否完整

思考

  1. char和varchar的区别?
    char的长度是固定的,字符不足用空字符填充。varchar的长度是可变的。除此之外,如果字段的字符集是是定长编码的字符集,比如ASCII,那么char类型的字段真实数据长度是不会记录在行格式的【变长字段长度列表中】
  2. 变长字段占用的存储空间哪两部分
    该字段占用的字节数和真正的数据内容
  3. 什么情况下会导致溢出列?
    一条记录的某个列中存储数据占用的字节数非常多的时候,会导致溢出列
  4. 为什么VARCHAR不能太长?
    VARCHAR的字段太长可能会导致溢出列
  5. 表的定义200个字段会有什么问题?
    可能容易发生溢出列。一个页16KB大小,字段越多,一个页存储的记录越少。