InnoDB记录存储结构
InnoDB简介
-
InnoDB将数据划分成若干个数据页,以页作为磁盘和内存之间交互的基本单位
-
查看 存储引擎页的大小 (默认
16KB)- show variables like 'innodb_page_size'
- show variables like 'innodb_page_size'
-
也就是一般情况下,一次最少从磁盘中读取
16KB的内容到内存中,一次最少把内存中的16KB刷新到磁盘中
InnoDB行格式
行格式的种类
- COMPACT 、REDUNDANT、DANAMIC、 COMPRESSED
指定行格式的语法
create table 表名 (列的信息) ROW_FORMAT = 行格式名称
COMPACT 行格式
- 变长字段长度列表
- 在mysql中 varchar(M)、VARBINARY(M)、各种TEXT、各种BLOB类型 。我们把这些数据类型称之为
变长字段。 - 变长字段中存储多少个字节的数据是不固定的,所以存储的时候需要存储2部分信息
- 真正的数据内容
- 该数据占用的字节数
- 变长字段长度列表
按照列的顺序逆序存放,比如变长字段n3的字节数(内容长度) ,变长字段n2的字节数 - 字节数用 16进制表示,比如 4个字节就是 0X04
比如 c1 ,c2,c4 3个变长字段的值分别是 'aaaa' ,'bbb','c' 对应的长度也就是 0X04 ,0X03,0X01 因此该行的数据格式也就是如下:
- 在mysql中 varchar(M)、VARBINARY(M)、各种TEXT、各种BLOB类型 。我们把这些数据类型称之为
其中 01 03 04之间是没有间隔的,只是为了清晰展示
- NULL值列表
一条记录中的某些列可能存储NULL值 ,如果把这些NULL值放到记录的真实数据中存储会很占地方。所以把一条记录中值为NULL的列统一管理起来。
处理过程如下:
- 统计表中运行存储NULL的列有哪些
- 如果表中没有允许存储NULL的列,则NULL值列表也就不存在了。
- 否则,每个允许存储NULL值的列对应一个二进制位,二进制位按照列的顺序逆序排列 1 表示值为NULL ,0 表示不为 NULL
- 记录头信息
- 由固定的 5个字节组成
- 预留位1
- 预留位2
- deleted_flag
- 标记该记录是否被删除
- min_rec_flag
- B+树的每层非叶子节点中最小的目录项记录都会添加该标记
- n_owned
- 一个页面中的记录会被分为若干个组,每个组中有一个记录是 “带头大哥”,其余的是小弟。 带头大哥记录的 n_owned的值代表改组有多少条记录
- heap_no
- 表示当前记录在叶堆中的相对位置
- record_type
- 表示当前记录的类型 0 普通记录 1 B+树非叶子节点的目录项记录 2 Infimum记录 3 Supremum记录
- next_record
- 下一条记录的相对位置
- 记录的真是数据
- row_id
- 是否必需 否 6个字节 行ID ,唯一标识一条记录
- trx_id
- 是否必需 是 6个字节 事务ID
- roll_pointer
- 是否必需 是 7个字节 回滚指针
- row_id
InnoDB主键生成策略
优先使用用户自定义的主键作为主键 无自定义主键,将选取一个 not null 的 UNIQUE键作为主键 上述条件都不满足,则自动为表添加一个 名为row_id的隐藏列作为主键
溢出列
如果某一条记录中列太多,某一列的数据在当前页放不下时就会产生溢出列
- COMPACT 、REDUNDANT
- 在当前列存储一部分数据,剩下数据存其他页。然后在当前列位置用 20个字节指向其他数据中存储剩余数据的地址
- DYNAMIC COMPRESSED
- 数据全部存其他页,在当前列位置用20个字节指向其他数据中存储剩余数据的地址
InnoDB数据页结构
数据页结构
- 文件头部
- 页面头部
- Infimum + Supermum
- 页面中的最小记录和最大记录 ,两个虚拟的记录
- User Records
- 用户记录,用户存储的记录内容
- 用户记录会进行分组,每个分组最多8条记录,每个对应对应页目录中一个槽
- Free Space
- 空闲空间 ,页中尚未使用的空间
- Page Directory
- 页目录,页中某些记录的相对位置
- 由槽组成,槽对应一个分组,记录该分组中的最后一条记录的页地址偏移量(这条记录的n_owned的值就是这个分组有多少条记录)
- File Trailer
- 文件尾部,校验页是否完整
在页中根据主键查找记录的方式
- 通过二分法确定该条记录所在分组对应的槽,并找到该槽所在分组中主键值最小的那条记录
- 通过记录的next_record 属性遍历该槽所在组的各个记录