mysql系列1---从存储的行数据开始

142 阅读4分钟

1:mysql的一行数据底层存储是怎样的?

我们以innoDB举例,他有4种,COMPACT,REDUNDANT,DYNAMIC和COMPRESSED。 简略图如下:

image.png 好,咱们看下这些什么意思。

可变长度是什么意思? 简单来说,我们在创建数据库类型的时候,比如int,bit,char(5),这种在创建数据表的时候,就已经定好了空间,就像你预订房间,在定的时候就已经指定了房间的大小,即便你后来不住,或者某个房间定的超级大,但实际只住了一个人,那也是同样的空间,所以我们平时设计表的时候int(5),int(10)实际没有啥用处,int就是占了4个字节,不管你放的几,只有在你设置了填充0的时候,他会帮你默认在左侧填上,比如你的int(5),存了1,就会变成00001,实际并不影响存储,也没什么用处。char特殊一点,根据编码格式以及存储内容,占用字节数都有所不同,比如char(5),如果是ascii编码,那就会预留5个字节,但如果是utf8,则预留5-15个字节,也算一种可变长度。

可变长度则不一样,在创建的时候并没有提前留空间,比如varchar,clob,你写varchar(50),并不会真的给你留150个字节在那。 我们举个例子,先创建一个表

image.png 然后往里面塞一些数据

image.png 这个时候再看下第一个图,可变长度就是01 03 04(跟存的字段是倒序的,以后说原因),可能你会纳闷这个长度有啥用?其实很简单,真实数据都是堆在一起的,就像仓库里现在都堆了无数盒子,你想找到哪个盒子是你的,如果能根据类型来,你是int,ok,你去从头开始找4个字节长度,这些都是你的数据。但是,不是所有类型都有定长,varchar就没有,他不一定存多少,所以他记录了一下,你应该走多少字节,找到你的数据(咱们完全可以猜测到,在你insert/update的时候,一定会数一下你当时插了多少字节,然后更新了这个可变长度的数据)。 至于为啥不直接把每个字段数据分开,用特殊标记隔离?这个很简单,因为你不能保证存储的数据不能有这些特殊标记,除非你拆开文件存储,不然只能记录长度来识别。(每个字段一个文件,想想都蠢)。记录这个长度的时候,一般1个字节就够了,如果最大字节超过255并且真实数据占用最大的一半,则需要两个字节。如果某个字段的值为null,则不会记录,因为没长度嘛。所以如果你的字段设计的时候都不是变长字段,或者值都是null,这一块就不会存数据。

接下来看null值列表,依旧是倒叙,这个很简单,一个字节8位,每个位可以代表0和1,也就是说1个字节可以表示8个允许为空的字段(not null的不会被统计在这里),如果字段超过8个并且都允许为空,则需要2个字节,以此类推。这个比较简单。

至于记录头的信息,固定5个字节,也就是40位,前两位暂时预留,第三位标识是否删除,其他暂时先不用了解。

当然除了我们自己的数据列,mysql也会帮我们创建一些隐藏列。比如行id(用来唯一标识行数据的,如果你有主键了,这个就不会有),事务id,回滚指针。分别是6,7,7字节。

REDUNDANT的话跟COMPACT区别在哪? 首先记录头的大小是6 字节,内容也有一些差别。尤其是char(5)这种存储,他不属于可变字符,直接按最大存。

大家都知道,mysql中数据都是以页为基本单位,一页是16k,也就是65535字节,但是很明显,我们一个字段都可能超过这个,所以这里有个溢出列的概念,意思就是我肯定存不下,我也不打算存,我只存一小部分数据或者干脆不存,然后存下放到其他数据页的地址就行。。如果一个DYNAMIC是不存真实数据,只存20字节溢出页地址,COMPACT则有768字节会存下真实数据,剩下20字节存溢出页地址。COMPRESSED顾名思义,压缩了,肯定更省空间,但是很明显,解析更耗费资源。