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会在当前页存储一部分数据信息和在其它页的剩余数据地址。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论