1、背景
mysql中数据存储是存储引擎干的事,InnoDB存储引擎以页为单位存储数据,每个页的大小为16KB,平时我们操作数据库都是以行为单位进行增删改查,行数据是存储在页上的,行的格式有4种:Compat、Redundant、Dynamic、Compressed,今天我们来讲一下Compat行格式。
2、数据示例
我们建表语句中使用的varchar、text、blob等类型不确定长度的就使用了Compact行格式,为了方便理解我们手动创建一个表并且插入两条记录,后面都结合这两条记录来进行Compat行格式讲解,表和记录如下:
创建表
CREATE TABLE test_compact
(
id INT AUTO_INCREMENT PRIMARY KEY,
str1 VARCHAR(255) NOT NULL DEFAULT '',
str2 VARCHAR(255),
str3 CHAR(5)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 ROW_FORMAT=COMPACT;
插入数据
INSERT INTO test_compact (str1, str2, str3)
VALUES ('AA', 'BB', 'CCC'),
('AAA', NULL, NULL);
查看数据
mysql [xxx]> select * from test_compact;
+----+------+------+------+
| id | str1 | str2 | str3 |
+----+------+------+------+
| 1 | AA | BB | CCC |
| 2 | AAA | NULL | NULL |
+----+------+------+------+
2 rows in set (0.001 sec)
查看表的行格式
mysql [xxx]> show table status like 'test_compact' \G;
*************************** 1. row ***************************
Name: test_compact
Engine: InnoDB
Version: 10
Row_format: Compact //目前表使用的行格式
Rows: 2
Avg_row_length: 8192
Data_length: 16384
Max_data_length: 0
Index_length: 0
Data_free: 0
Auto_increment: 3
Create_time: 2024-12-13 17:20:47
Update_time: 2024-12-13 17:21:02
Check_time: NULL
Collation: utf8mb4_general_ci
Checksum: NULL
Create_options: row_format=COMPACT //创建表时指定的格式
Comment:
Max_index_length: 0
Temporary: N
1 row in set (0.001 sec)
3、Compact解释
【1】组成
Compact行格式组成图如下:
Compact行由头部信息+隐藏行+数据列三部分组成。
头部信息:由三部分组成,可变长度列的实际长度信息、列是否为NULL信息、描述记录信息。
隐藏列:有三列,DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR,后面详细讲解。
数据列:每一列的值。
【2】头部信息
头部信息可以如图表示:
接下来就结合上面创建的test_compact表和插入的记录进行理解。
可变长度列指的就是表中的str1和str2字段,因为str1和str2长度不固定,所以需要额外花费空间来记录str1和str2的长度,str1和str2的值只有不为NULL的时候才需要记录长度,并且str1和str2长度的顺序是根据列顺序的逆序来存放。
存放str1和str2长度大小为1个或者2个字节,判断方法是:可变字段存储的最大字节>255字节并且真实存储的字节数>127字节就使用2个字节存储长度,否则使用1个字节存储长度。
可变字段存储的最大字节=varchar(n)里的n * 字符集的最大字节数。n代表的是字符数,而我们用的utf8mb4字符集1个字符用1~4个字节表示,可以用如下命令看:
mysql [xxx]> show charset like 'utf8mb4';
+---------+---------------+--------------------+--------+
| Charset | Description | Default collation | Maxlen |
+---------+---------------+--------------------+--------+
| utf8mb4 | UTF-8 Unicode | utf8mb4_general_ci | 4 |
+---------+---------------+--------------------+--------+
1 row in set (0.001 sec)
可以看到utf8mb4可以表示的一个字符最大字节为4,所以str1和str2列可以存储的最大字节数为255 * 4 = 4080,ASCII字符只占用1个字节,第一条记录中的str1和str2实际长度都为2个字节。
同样的str3的类型为CHAR(5),根据字符集决定存它储字节的范围为5~20,所以str3的长度信息也需要进行存储。
第一条记录str3内容’CCC’长度看起来虽然为3,实际上长度为5,这是为了防止更新str3为大于3小于5的数据时,以前的空间就成为了空间碎片需要释放。
可以用下图表示上面2条记录长度的存储信息:
可以为NULL列表指的就是没有NOT NULL修饰的str2和str3列,用二进制的0和1来表示列是否为NULL值,0代表非NULL,1代表NULL,根据列的顺序逆序排列NULL信息,必须用二进制的整数倍来存储NULL信息,不足1字节高位补0,上面2条记录的NULL信息如下:
行记录描述信息是由5个字节也就是40个二进制位组成,不同位代表不同的含义,不做过多解释。
【3】隐藏列
InnoDB存储引擎会为每一行数据额外分配三个列:DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR,通过表格来理解一下:
隐藏列 | 描述 | 大小 |
---|---|---|
DB_ROW_ID | 唯一标识 | 6字节 |
DB_TRX_ID | 事务id | 6字节 |
DB_ROLL_PTR | 指向要回滚数据的地址 | 7字节 |
DB_ROW_ID只有主键和unique健都不存在时,InnoDB存储引擎会创建一个大小为6字节作为隐藏主键id使用。
【4】数据列
数据列只需要注意值为NULL的列不需要再去花空间去存储,因为头部信息的NULL列表已经存储了是否为NULL,所以上面2条记录完整表示如下:
4、总结
本篇文章讲了Compact行格式组成,其它几种格式都差不多,Redundant会记录所有列的长度信息,通过相邻列的偏移量来计算列的长度;Dynamic、Compressed格式在行溢出(列数据过于大页面存不下)时列数据只存储其它页的地址,数据全存储在其它页通过链表连接;Compact和Redundant会在当前页存储一部分数据信息和在其它页的剩余数据地址。