25、对于VARCHAR这种边长字段,在磁盘上到底是如何存储的?
1、一行数据在磁盘上存储的时候,包含哪些东西?
上一章我们已经学习了,一行数据在磁盘上存储的时候,其实不仅仅是包含我们想象的那一点数据,他还包含了很多其他的信息,之前告诉大家,一行数据的存储格式大致如下所示。
说白了,就是除了每一个字段的值以外,他还包含了一些额外的信息,这些额外的信息就是用来描述这一行数据的。今天我们就详细的学习这些额外的信息里都是放了什么东西。
2、变长字段在磁盘中是怎么存储的?
大家都知道,在MySQL里有一些字段的长度是变长的,是不固定的,比如VARCHAR(10)之类的这种类型的字段,实际上他里面存放的字符串的长度是不固定的,有可能是“hello”这么一个字符串,也有可能是 ”a“ 这么一个字符串。
好,那么现在我们来假设一下,先有一行数据,他的几个字段的类型为VARCHAR(10),这个长度是可能变化的,所以这一行数据可能就是类似于:hello a a,这样子,第一个字段的值是 “hello” ,后面两个字段的值都是一个字符,就是一个a
然后另外一行数据,同样也是这几个字段,他的第一个字段的值可能是 “hi”,后面两个字段也是 ”a“, 所以这一行数据可能是类似于:hi a a 。一共三个字段,第一个字段的长度是不固定的,后面两个字段的长度都是固定的1个字符。
相比这个道理大家都能理解吧?
那么现在,我们来假设你把上述两条数据写入了一个磁盘文件里,两行数据是挨在一起的,那么这个时候在一个磁盘文件里可能有下面两行数据:
大家可以看到,两行数据在底层磁盘文件里是不是挨着存储的?
没错,其实平时你看到的表里的很多行数据,最终落地到磁盘里的时候,都是上面那种样子的,一大坨数据放在一个磁盘文件里都挨着存储的。
3、存储在磁盘文件里的变长字段,为什么难以读取?
现在我们来继续思考一个问题,假设现在我们要读取上面的磁盘文件里的数据,要读取出来hello a a这一行数据。那你觉得是那么容易的吗?
当然不是了,这个过程比你想想的可能要困难一些。
假设现在你要读取hello a a这行数据,第一个问题就是,从这个磁盘文件里读取的时候,到底哪些内容是一行数据?我不知道啊
因为这个表里的第一个字段是VARCHAR(10)类型的,第一个字段的长度是多少我们是不知道的
所以有可能你读取出来 “hello a a hi” 是一行数据,也可能是你读取出来 ”hello a” 是一行数据,你在不知道一行数据的每个字段到底是多少长度的情况下,胡乱的去读取是不现实的,根本不知道磁盘文件里混成一坨的数据里,哪里数据是你要读取的一行?
4、引入变长字段的长度列表,解决一行数据的读取问题
所以说才要在存储每一行数据的时候,都保存一下他的变长字段的长度列表,这样才能解决一行数据的读取问题。
也就是说,你在储存 “hello a a” 这行数据的时候,要带上一些额外的附加信息,比如第一块就是他里面的变长字段的长度列表。
也就是说,这个hello是VARCHAR(10)类型的变长字段的值,那么这个 “hello” 这个字段值的长度到底是多少?
我们看到 “hello” 的长度是5,十六进制就是0x05,所以此时会在 “hello a a” 前面补充一些额外信息,首先就是变长字段的长度列表,你会看到这行数据在磁盘文件里存储的时候,其实是类似如下的格式:0x05 null值列表 数据头 hello a a.
你这行数据存储的时候应该是如上所示的。
这个时候假设你有两行数据,还有一行数据可能就是:0x02 null值列表 数据头 hi a a,两行数据放在一起存储在磁盘文件里,看起来是如下所示的:
5、引入变长字段长度列表后,如何解决变长字段的读取问题?
所以假设此时你要读取 “hello a a” 这行数据,你首先会知道这个表里的三个字段的类型是VARCHAR(10) CHAR(1) CHAR(1),那么此时你首先要读取第一个字段的值,那么第一个字段是变长的,到底他的实际长度是多少呢?
此时你会发现第一行数据的开头有一个变长字段的长度列表,里面会读取到一个0x05这个十六进制的数字,发现第一个变长字段的长度是5,于是按照长度为5,读取出来第一个字段的值,就是“hello”
接着你知道后续两个字段都是CHAR(1),长度都是固定的一个字符,于是此时就依次按照长度为1读取出来后续两个字段的值,分别是 “a” “a”,于是最终你会读取出来" hello a a "这一行数据
接着假设你要读取第二行数据,你先看一下第二行数据后的变长字段长度列表,发现他第一个变长字段的长度是0x02,于是读取长度为2的字段值,就是“hi”,再读取两个长度固定位1的字符值,都是”a“,此时读取出来”hi a a "这行数据.
6、如果有多个变长字段,如何存放他们的长度?
接着我们假设,如果说有多个变长字段,如何存放他们的长度?
比如一行数据有VARCHAR(10) VARCHAR(5) VARCHAR(20) CHAR(1) CHAR(1),一共5个字段,其中三个是变长字段,此时假设一行数据是这样的:hello hi hao a a
此时在磁盘中存储的,必须在他开头的变长字段长度列表中存储几个变长字段的长度,一定要注意一点,他这里是逆序存储的
也就是说先存放VARCHAR(20)这个字段的长度,然后存放VARCHAR(5)这个字段的长度,最后存放VARCHAR(10)这个字段的长度。
现在hello hi hao三个字段的长度分别是0x05 0x02 0x03,但是实际存放在变长字段长度列表的时候,是逆序放的,所以一行数据实际存储可能是下面这样的:
7、思考
为什么MySQL在把一行一行的数据存储在磁盘上的时候,要采取这种“0x05 null值列表 数据头 hello a a 0x02 null值列表 数据头 hi a a”很多行数据都仅仅挨在一起的方式?
为什么MySQL不能用Java里面的序列化的那种方式?把很多行的数据做成一个大的对象,然后给他序列化一下写入到 磁盘文件里,从磁盘里读取的时候压根儿不用care什么行存储格式,直接反序列化一下,把数据就可以从磁盘文件里 拿回来了。
请大家思考一下,MySQL用这种数据紧凑挨在一起的方式来存储数据,到底有什么好处?