3.4 记录为NULL,innodb如何处理?——NULL值列表
能仔细看到这里,你肯定是个高手了。如果你和我一样开发规范中不推荐NULL,一般都写NOT NULL,其实记录中就不存在NULL值列表了,也节省了空间。
如果表中的某些列可能存储NULL值,把这些NULL值都放到「记录的真实数据」中存储会很占地方,所以dynamic行格式把这些值为NULL的列统一管理起来,存储到NULL值列表中,它的处理过程是这样的:
- 统计表中允许存储
NULL的列有哪些。
主键列、被NOT NULL修饰的列都是不可以存储NULL值的,所以在统计的时候不会把这些列算进去。比方说表test的3个列c1、c3、c4都是允许存储NULL值的,而c2列是被NOT NULL修饰,不允许存储NULL值。
- 「如果表中没有允许存储
NULL的列,则NULL值列表也不存在了」,否则将每个允许存储NULL的列对应一个二进制位,二进制位按照列的顺序「逆序排列」。二进制位的值为1时,代表该列的值为NULL,为0时,代表该列的值不为NULL。因为表test的c1、c3、c4都是允许存储NULL值的允许为NULL的列,所以这3个列和二进制位的对应关系就是这样:
NULL值列表必须用整数个字节的位表示,如果使用的二进制位个数不是整数个字节,则在字节的「高位补0」。
也就是说,表test只有3个字段允许为NULL,对应3个二进制位,不足1字节,那么就在高位补0即可。
以此类推,如果表中有9个字段都允许为NULL,那么这个记录的NULL值列表就需要2个字节来表示,高字节高位补0。
对于「第一条记录」,c1、c3、c4都不为NULL,对应的为进制位为0,十六进制表示就是0x00。
对于「第二条记录」,c3、c4都是NULL,对应的二进制位为1,十六进制表示就是0x06
这两条记录在填充了NULL值列表后示意图如下:
3.5 某个列数据占用的字节数非常多怎么办?——dynamic行格式的溢出列
如果某个列中存储的数据占用的字节数非常多,该列就可能称为溢出列。
对于占用存储空间非常多的列,在记录真实数据时,「该列只会用20字节空间」,而这20字节的空间不存储数据,因为数据都分散存储在其他几行中了。这20字节的空间存储的是分散行的地址和占用的字节数。分散行记录是单链表连接的结构。
❝
后续:如果大家对innodb存储结构其他行格式感兴趣,或者我没说的记录头信息,可以去阅读《MySQL是怎样运行的》一书,我和书中不同的是,书中讲的Compact格式,字符集是ascii,我选用的是平时开发中用到的默认dynamic格式,字符集是utf8mb4,字符集变化后所有的数据我在文中和图中都有重新计算。大家平时或许没关注过行格式,那么就是按照dynamic格式理解就可以,更贴近实际开发。




