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格式理解就可以,更贴近实际开发。