首先需要注意的是,存储引擎本就是针对表而言的。
知识在这本书中《MySQL的技术内幕 InnoDB存储引擎 第二版》
链接:https://pan.baidu.com/s/10SGjh1P6dEF9u3haHZRzAg?pwd=5don
提取码:5don
从 InnoDB 存储引擎的逻辑存储结构上看,所有的数据都被逻辑地存放在一个空间中,称为表空间。表空间又由段(segment,如索引段、数据段、回滚段等等)、区(extent)、页(page)组成。
表空间可以看做是 InnoDB 存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。(表空间其实就是个抽象概念,本质上就是文件)
(1)系统表空间
innodb_data_file_path
】控制,默认值是ibdata1:12M:autoextend
(文件名为ibdata1、12MB、自动扩展)。
下图说明了 Innodb 默认情况下是一些表信息放在独立表空间中,一些信息放在系统表空间中。
从上面的 InnoDB 逻辑存储结构图中可以看出,表空间是由各个段组成的,常见的段有数据段、索引段、回滚段等。数据段即为 B+ 树的叶子节点(Leaf node segment),索引段即为 B+ 树的非叶子节点(Non-leaf node segment)。
区是由连续页组成的空间,在任何情况下每个区的大小都为1MB。为了保证区中页的连续性,InnoDB 存储引擎第一次从磁盘申请 4~5 个区。在默认情况下,InnoDB 存储引擎页的大小为 16KB,即一个区中一共有 64 个连续的页。
注意:在用户启动了参数innodb_file_per_table
后,创建的表默认大小是96KB。明显和区中连续64个页不符,其实这是因为在每一个段开始时,先会去用 32 个页大小的碎片页来存数据,使用完之后才会去申请 64 个连续页。
页是 InnoDB 磁盘管理的最小单位(也可以称为块)。在 InnoDB 存储引擎中,默认每个页的大小为 16KB。
在 InnoDB 存储引擎中,常见的页类型有:
从上面的 InnoDB 逻辑存储结构图中可以看出,页中数据记录是以行的形式作为存储的,意味着页中保存着表中的一行一行的数据。
指定行格式的语法如下(关键字 ROW_FORMAT
)
-- 创建数据表时,显示指定行格式
CREATE TABLE 表名 (列的信息) ROW_FORMAT=行格式名称;
-- 创建数据表时,修改行格式
ALTER TABLE 表名 ROW_FORMAT=行格式名称;
-- 具体如下:
CREATE TABLE `ydl_user` (
`user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户账号',
....
PRIMARY KEY (`user_id`) USING BTREE
) ROW_FORMAT = DYNAMIC;
Compact 行记录以如下方式进行存储
BitMap
中,这样可以节省很多空间,NULL值标志位也是逆序排列,占用空间按照字节数高位补零,如有九个字段可以为空(00000001 01010101)。create table test (
t1 varchar(10),
t2 varchar(10),
t3 char(10),
t4 varchar(10)
) engine=innodb row_format=compact;
insert into row_test values('a','bb','bb','ccc');
insert into row_test values('d','ee','ee','fff');
insert into row_test values('d',NULL,NULL,'fff');
节选对应的【真实的表空间】中的的二进制结构表示(使用 hexdump -C .ibd表空间
命令查看):
2c 00 00 00 00 2b 68 00 00 00 00 06 05 80 00 00
00 32 01 10 61 62 62 62 62 20 20 20 20 20 20 20
20 63 63 63 03 02 01 00 00 00 18 00 2b 00 00 00
00 02 01 00 00 00 00 0f 62 c9 00 00 01 b2 01 10
64 65 65 65 65 20 20 20 20 20 20 20 20 66 66 66
03 01 06 00 00 20 ff 98 00 00 00 00 02 02 00 00
00 00 0f 67 cc 00 00 01 b6 01 10 64 66 66 66
第一行整理如下,需要注意,我们有三个变长列varchar:
03 02 01 // 变长字段长度列表,逆序,t4列长度为3,t2列长度为2,t1列长度为1
00 // NULL标志位,第一行没有NULL值
00 00 10 00 2c // 记录头信息,固定5字节长度
00 00 00 00 2b 68 // RowID我们建的表没有主键,因此会有RowID,固定6字节长度
00 00 00 00 06 05 // 事务ID,固定6个字节
80 00 00 00 32 01 10 // 回滚指针,固定7个字节
61 // t1数据'a'
62 62 // t2'bb'
62 62 20 20 20 20 20 20 20 20 // t3数据'bb' Ox20十进制是32对应ascii码是空字符
63 63 63 // t4数据'ccc'
第二行整理如下:
03 02 01 // 变长字段长度列表,逆序,t4列长度为3,t2列长度为2,t1列长度为1
00 // NULL标志位,第二行没有NULL值
00 00 18 00 2b // 记录头信息,固定5字节长度
00 00 00 00 02 01 // RowID我们建的表没有主键,因此会有RowID,固定6字节长度
00 00 00 00 0f 62 // 事务ID,固定6个字节
c9 00 00 01 b2 01 10 // 回滚指针,固定7个字节
64 // t1数据'd'
65 65 // t2数据'ee'
65 65 20 20 20 20 20 20 20 20 // t3数据'ee'
66 66 66 // t4数据'fff'
第三行整理如下:
03 01 // 变长字段长度列表,逆序,t4列长度为3,t1列长度为1
06 // 00000110 NULL标志位,t2和t3列为空
00 00 20 ff 98 // 记录头信息,固定5字节长度
00 00 00 00 02 02 // RowID我们建的表没有主键,因此会有RowID,固定6字节长度
00 00 00 00 0f 67 // 事务ID,固定6个字节
cc 00 00 01 b6 01 10 // 回滚指针,固定7个字节
64 // t1数据'd'
66 66 66 // t4数据'fff'