MySql数据是如何存储在磁盘上存储的?
-
server层格式:与存储引擎无关,Binlog存储常用的一种 (Bin Log 我们前面已经详细介绍过了,这个是MySql主从复制的一个很重要的文件) -
索引元组格式:InnoDB存取过程记录的中间状态,是InnoDB在内存中存储的格式 (换句话说我们的增删改的操作都是在内存中执行的,这个只是一种临时状态) -
物理存储格式:记录在物理页面中的存储格式,即compact格式,与索引元组格式一一对应。(这个是数据在磁盘存储的真正的格式)
SHOW TABLE STATUS
查看到行的的存储格式。
varchar
为例,假设现在三个字段,字段类型分别为:
varchar(10),char(1),char(1)
,
char
大家都是知道的,存储的基本是一些已知的长度固定的数据,假设这三个类型的字段分别有如下的数据:
mysql
a
a
;第二行:
dog
b
c
;画个图来帮助大家想象,现在你看到的是数据中为我们展现的样子。
mysql
a
a
dog
b
c
,他们在磁盘中都是挨在一起存储的。
MySql
在设计的时候才会使用行格式存储,才会有前面的哪些变长字段列表和标志位以及记录信息,这些就是用来记录一行的记录的信息,换句话说,
MySql
是通过这些描述信息来定位到一行中的具体记录的。
MySql
是很清楚的,在这个基础上我们能看明白下面和想通后面的事情。首先我们看到
mysql
是5个字符,使用十六进制表示是 0x05,所以他的存储大概是这样子的:
MySql
这个时候是怎么读读取数据的了,就是他会先根据变长字段长度列表中描述的变长字段的信息去查找变长字段,例如第一行,
MySql
解析到变长字段是5,所以他会在
mysql
a
a
dog
b
c
这些里面取出5个字符,也就是
mysql
,紧接着后面是两个 char(1) 也就是两个
a
在依次取出来。
中间设备。由浅入深,我们慢慢来,刚刚上面说到的仅仅是一种非常简单的情况,这个首先是帮助大家理解,让大家先明白有这么个回事,是这么回事,然后在慢慢的挖掘,我们一定要一个萝卜一个坑的去踏实学习
varchar
类型的字段怎么办?例如:
varchar(3),varchar(10),varchar(4),char(1)
,他有一条记录是这样子的:
aaa
,
bb
,
cccc
,
d
,你根据上面的能推测出磁盘中的行记录是怎么样子的吗?
0x03,0x02,0x04
null标志位 记录头信息
aaa
bb
cccc
d
;这么想的同学请鼻子靠墙:);实际上并不是这样子的。
MySql
在 compact 行格式中,把所有变长类型的长度存放在行记录的开头部位形成一个列表(这个列表就是刚刚上面说的变长字段列表),按照列的逆序存放,也就是大致是这样子的:
next_record
指针 指向下一行
记录头信息
和
真实数据
之间的位置。因为这个位置刚刚好,向左读取就是行描述相关信息,向右读取就是真实数据。正好对应变长字段长度列表。画个图来帮助大家理解下:
-
数据在磁盘中的存储在物理空间上面是连续的 -
数据是被存放在MySql设计出来的数据页上面的,数据页上面存储的才是最终的一行一行的记录 -
行的存储格式默认是Compact -
每一行数据都会有相应的行描述部分,描述部分有【变长字段列表】【NULL标志位】【记录头信息】 -
每一行都会有next_record指针,指向记录头和变长字段列表的中间某个位置,方便寻址 -
变长列表中的varchar列的描述是逆序的(和字段的顺序相反)这样做的目的在上图中描述的很清楚了
MySql
又是怎么处理的呢?是不是直接存储NULL呢。
MySql
针对与Null直接存储,他实际上是按照“NULL”这样字符串的形式存储的,这样显然不行啊,因为字符串要占用空间的啊(一个 NULL 字符串要占用四个字符呢),你都没有值,还占这么多空间,所以
MySql
肯定不是这样存储的。其实
MySql
在处理NULL值的时候是会将它通二进制来存储的,且也是逆序的
CREATE TABLE `students` (
`name` varchar(10) NOT NULL,
`address` varchar(255) DEFAULT NULL,
`gender` char(1) DEFAULT NULL,
`class` varchar(10) DEFAULT NULL,
`hobbies` varchar(255) DEFAULT NULL,
PRIMARY KEY (`name`)
)
0x08 0x05
-
name字段是主键,不可能在NULL 标志位中的,又因为 name 是varchar 字段,所以就会去变长字段列中查找,找到值为 0x05 接着就会去字段列表中读取5个字符的长度,也就是 roles ,第一个字段读取成功; -
接着是 address 字段,因为类型是 MySql 已知的,又因为字段值为 null 所以就不需要去读取了,第二个字段读取结束; -
接着是gender字段,是char类型的,直接拿到 f 就可以了; -
下一个是class 字段,因为是null 所以根本不会去变长字段中查找; -
最后一个是 hobbies 字段,因为不为null ,又是第二个变长字段,这个时候就会去 变长字段列表中查找,结果定位到是 0x08 那就读取 8 个字符的长度出来,拿出来是hobby_xx;
0x08 0x05
00000101
0000010100000000000000000000000000000010
21134
44
232343