写点什么

验证一个小小的问题

作者:艾小仙
  • 2022 年 9 月 06 日
    上海
  • 本文字数:1548 字

    阅读完需:约 5 分钟

在之前的文章提到过一个问题,而且网上很多文章也是这么说的,前几天有人对这个问题提出了一点不同的意见,抱着谨慎的态度做了一个测试。


问题是这样的:COMPACT 格式下,NULL 值列表是否一定会占用一个字节的空间?


对于这个问题,我的回答和网上很多回答是一样的,如果都是 NOT NULL 就不会有 NULL 值列表,所以不会占用,反之则会占用。


今天,就对这个问题做一个验证。

存储空间

先回顾一下之前的知识。


数据库中的一行记录在最终磁盘文件中也是以行的方式来存储的,对于 InnoDB 来说,有 4 种行存储格式:REDUNDANTCOMPACTDYNAMICCOMPRESSED


InnoDB 的默认行存储格式是COMPACT,存储格式如下所示,虚线部分代表可能不一定会存在。



变长字段长度列表:有多个字段则以逆序存储,我们只有一个字段所有不考虑那么多,存储格式是 16 进制,如果没有变长字段就不需要这一部分了。


NULL 值列表:用来存储我们记录中值为 NULL 的情况,如果存在多个 NULL 值那么也是逆序存储,并且必须是 8bit 的整数倍,如果不够 8bit,则高位补 0。1 代表是 NULL,0 代表不是 NULL。如果都是 NOT NULL 那么这个就存在了,每多 8 个 NULL 会多占用一个字节的空间。


ROW_ID:一行记录的唯一标志,没有指定主键的时候自动生成的 ROW_ID 作为主键。


TRX_ID:事务 ID。


ROLL_PRT:回滚指针。


最后就是每列的值。


为了说明清楚这个存储格式的问题,我弄张表来测试,这张表只有c1字段是 NOT NULL,其他都是可以为 NULL 的。



可变字段长度列表c1c3字段值长度分别为 1 和 2,所以长度转换为 16 进制是0x01 0x02,逆序之后就是0x02 0x01


NULL 值列表:因为存在允许为 NULL 的列,所以c2,c3,c4分别为 010,逆序之后还是一样,同时高位补 0 满 8 位,结果是00000010


其他字段我们暂时不管他,最后第一条记录的结果就是,当然这里我们就不考虑编码之后的结果了。



这样就是一个完整的数据行数据的格式,反之,如果我们把所有字段都设置为 NOT NULL,并且插入一条数据a,bb,ccc,dddd的话,存储格式应该这样:


测试

这里存在一点点小问题,首先我看到了阿里的数据库月报中的测试和描述。


从这段代码看出之前的猜想,也就是并不是 Null 标志位只固定占用 1 个字节==,而是以 8 为单位,满 8 个 null 字段就多 1 个字节,不满 8 个也占用 1 个字节,高位用 0 补齐


他的意思是无论如何都会占用一个字节,但是看了他的测试,发现他的表是允许 NULL 的,所以他的这个测试无法说明我们要验证的问题。


按照网上大佬给出的方案,创建表,然后插入测试数据,数据库中存在 NULL 值。


 CREATE TABLE test ( c1 VARCHAR ( 32 ),   c2 VARCHAR ( 32 ),   c3 VARCHAR ( 32 ),   c4 VARCHAR ( 32 ) ) ENGINE = INNODB row_format = compact;
复制代码


使用命令SHOW VARIABLES LIKE 'datadir'找到 ibd 文件位置。



使用命令转换 ibd 文件为 txt 文件。


hexdump -C -v test.ibd > /Users/irving/test-null.txt
复制代码


打开文件找到 supremum 部分。



不用看那么多,就看一部分:


03 02 02 01 是上面说的变长字段长度列表,以为我们有 4 个字段,所以 4 个字节。

00 就是 NULL 标志位

00 00 10 00 25 是数据头 5 个字节


这个肯定没有问题,然后再次创建一张表,这时候字段都是 NOT NULL,然后再次执行命令。


 CREATE TABLE test ( c1 VARCHAR ( 32 ) NOT NULL,   c2 VARCHAR ( 32 ) NOT NULL,   c3 VARCHAR ( 32 ) NOT NULL,   c4 VARCHAR ( 32 ) NOT NULL ) ENGINE = INNODB row_format = compact;
复制代码


拿到另外一个 ibd 文件。



对比其实很清楚能发现问题,这时候已经没有了 NULL 值列表的标志位了。


SO,这个测试结果证明,如果存在任意 NULL 值,NULL 值列表至少占用一个字节的空间,以后每多 8 个 NULL 值多占用一个字节,如果都是 NOT NULL,则不会存在 NULL 值列表标记,不占用空间。


巨人的肩膀:


http://mysql.taobao.org/monthly/2016/08/07/

https://www.cnblogs.com/zhoujinyi/archive/2012/10/17/2726462.html

发布于: 刚刚阅读数: 3
用户头像

艾小仙

关注

公众号:艾小仙。阿里P7,编程修仙 2020.09.01 加入

公众号:艾小仙

评论

发布
暂无评论
验证一个小小的问题_Java_艾小仙_InfoQ写作社区