最近和一些小伙伴一起在研究 MySQL
相关的理论知识,看磁盘存储的信息其实可以自己手动实践一下的,这个其实很简单,但有些小伙伴没有这方面的知识,不知道怎么上手。我这里写一篇简单的教程,我使用的是 8.0.23
版本。
1. 创建数据库和表
首先,我们创建一个简单的表来存储数据。执行以下 SQL 语句:
CREATE TABLE `test10` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(10) NOT NULL DEFAULT '',`age` int NOT NULL DEFAULT '0',`sex` char(3) NOT NULL DEFAULT 'N',`text1` varchar(100) NULL DEFAULT NULL,`text2` varchar(100) NULL DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
创建完表之后,我们可以到 MySQL
的数据文件目录下找到一个名字叫 test10.ibd
的文件。
如果不知道 MySQL
的数据文件目录,可以执行 show variables like 'datadir';
查看。我的数据库名称是 test1
,这个目录下就看到了创建的表的 ibd
文件。
2. 插入数据并查看磁盘存储
接下来,我们向表中插入一条数据:
INSERT INTO `test10` (`name`, `age`, `sex`, `text1`, `text2`) VALUES('yuan01', 0, 'N', 'aaa', 'bbbb');
使用 hexdump
命令查看该文件内容:
hexdump -C test10.ibd
3. 分析数据存储结构
忽略不认识的内容,快速跳到我们熟悉的数据部分。只插入了一条数据的情况下,我们可以直接拉到文件底部。
这是我拉到最底下的截图,红色框内的就是聚簇索引
,也是表数据
;蓝色框内的就是辅助索引
。
这个截图中,每两个字符代表一个字节,它们是十六进制的。比如说,为什么我知道
79 75 61 6e 30 31
就是yuan01
?
首先,可以通过右边看到;其次,这是 ASCII 码。我们可以拿 ASCII 对照表来看,直接对照右边的内容,更容易识别。
用浅色小框框住的 45bf
表示该页的类型是索引页。因此,在 InnoDB
中,表数据和索引数据类型都属于索引页。如果一张表没有辅助索引,那么它就只有一个聚簇索引,也就是一个 B+ 树;如果有辅助索引,就会有两个 B+ 树(包括了聚簇索引的)。
我们所说的“不需要回表”,意思是只操作索引。我们从这个视角来看,辅助索引也可以看做是一个独立的数据表,它的主键是索引列,而字段则是索引列和主键字段的组合。
接下来,我们来看数据:
红色框 聚簇索引部分
黄色横线 标出的 yuan01
就是我们的 name
字段。
蓝色横线 标出的 Nxx
是 sex
字段,其中 x
代表空格,空格显示不出来。这里,我们可以看到,CHAR
类型的字段会用空格填充至固定长度。20
是 ASCII
码对应的空格,20
是十六进制值表示,而十进制是 32
。
绿色横线 标出的部分是 VARCHAR
类型字段的字节数记录。在这里,我们可以看到 VARCHAR
在存储时会占用多少字节。在这个例子中,这里的字节数占了 4 个字节,我们有 3 个 varchar
类型,1 个 char
类型,CHAR
类型字段也会在这里标识出它的长度。
这些长度信息是倒序存储的,04 03 03 06
代表了各个字段的字节数:bbbb
占了 4 个字节,aaa
占了 3 个字节,Nxx
占了 3 个字节,yuan01
占了 6 个字节。如果我们使用中文字符,比如 测试aa
,它将占用 8 个字节,因为使用的是 utf8mb4
字符集,每个中文字符占 3 个字节。
为什么绿色横线的位置保存长度而不是其他地方呢?因为在主键前的 6 个字节是行记录头,这是
InnoDB
定义的存储格式。
红色横线 标出的是主键ID
,至于为什么最高位是 1,这点我还没有完全搞明白。
为什么这个位置是
主键ID
?因为主键后面会有 13 个字节,这些字节分别存储事务ID
和回滚指针
。因此,通过从yuan01
开始往前数,我们就能找到主键ID
。
蓝色框 辅助索引部分
这个简单点两个红色横线标出的分别是 索引列值
和 主键 ID
。
其他的一些细节,可以通过实际操作自行验证。我的理解就到这里了。