一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情
InnoDB行格式
InnoDB的4种不同类型的行格式,COMPACT、REDUNDANT、DYNAMIC和COMPRESSED
COMPACT行格式
COMPACT行格式分两个部分,一部分存储记录的额外信息,另外一部分存储记录的真实数据。
记录的额外信息
- 变长字段长度列表:在COMPACT行格式中,所有变长字段的真是数据占用字节数都存放在记录的开头位置,从而形成变长字段长度列表,各变长字段的真实数据占用的字节数按照列的顺序逆序存放
- NULL值列表:表中没有允许存储NULL的列,则NULL值列表不存在
凡设计,必有考量。 我们来考虑变长字段长度列表与NULL值列表的作用以及作者为什么这么设计。
- 为什么需要变长字段长度列表?
当存在格式为
VARCHAR
的列时,需要在行的头部记录VARCHAR
的长度,这便是变长字段长度列表存在的原因。 - 站在设计者的角度,是否存在其他的设计呢?
假如我们不在头部使用变长字段长度列表,我们可以在
VARCHAR
列使用额外的空间来记录该列数据的大小。例如我们可以规定前n个字节用来记录该数据的大小。这个n个大小就有考究了,理论上,MySQL并没有对VARCHAR
能存放的数据大小作一个限制,因此我们这个n并不太好设计;从占用空间的角度考虑,假如我们每个VARCHAR
都使用额外的n个字节来记录数据大小,这是十分占用空间的。
又是一个时间换空间的经典案例。
- 为什么会有NULL值列表? 记录中可能存在NULL值,假如所有列都存实际的NULL值,会十分的占用空间;
记录的真实数据
MySQL会为每个记录默认地添加一些列(隐藏列)
列名 | 是否必需 | 占用空间 | 描述 |
---|---|---|---|
row_id | 否 | 6字节 | 行ID,唯一标识一条记录 |
trx_id | 是 | 6字节 | 事务ID |
roll_pointer | 是 | 7字节 | 回滚指针 |
隐藏列补充知识:InnoDB表的主键生成策略:用户设置优先;无则选用不允许存储NULL值的UNIQUE键,否则默认添加row_id的隐藏列作为主键。 注意,由隐藏列补充知识可推理,row_id不是必须生成的。
REDUNDANT行格式
与COMPACT行格式类似,REDUNDANT行格式也分为两个部分,这其中,记录的额外信息有区别,REDUNDANT行格式记录的是字段长度偏移列表,而COMPACT行格式记录的是变长字段长度列表以及NULL值列表。
- 字段长度偏移列表:REDUNDANT逆序存储所有字段的长度偏移列表,包括隐藏列
- NULL值处理:在NUUL值处理方面,REDUNDANT行格式使用字段长度偏移列表在处理NULL值,即在字段长度偏移列表中,将列对应的偏移量值得第一个比特位作为是否为NULL的依据,也被称为NULL比特位。此时,在设计角度考虑,作者使用了一点空间来换取时间。
溢出列
在COMPACT和REDUNDANT行格式中,对于占用存储空间非常多的列,在记录的真实数据处只会存储该列的一部分数据(768字节),而把剩余的数据分散存储在几个其他的页中,然后记录的真实数据处用20字节存储指向这些页的地址,从而可以找到剩余数据所在的页
为什么使用20字节存储指向这些页的地址?
我没有找到相关的解释,我这边讲一下自己的理解:
- 当20字节只用来指向一个数据页时,寻址能力远超当前计算机的存储能力;
- 当20字节被用来指向多个数据页时,就需要对20字节进行拆分,寻址能力出现下降。 20字节应该是作者检验过的,寻址能力以及指向页数的平衡点。
DYNAMIC行格式和COMPRESSED行格式
- DYNAMIC在COMPACT基础上,优化了处理溢出列的方式,不会在真实数据处存储溢出数据的前768字节,而是全部存储到溢出页中。
- COMPRESSED行格式在DYNAMIC行格式的基础上,会使用压缩算法对页面进行压缩,以节省空间
实际工作时,我们该如何选择呢?
- 从性能角度考虑(越好越靠前) REDUNDANT > COMPACT > DYNAMIC > COMPRESSED
- 从占用资源考虑(占用越少越靠前) COMPRESSED > COMPACT ~= DYNAMIC > REDUNDANT
由此可以看到,MySQL默认的COMPACT其实是性能与占用资源都较好的行格式。实际业务中,我们应该依据实际的场景来选择最佳行格式。