简介:今天被问一个问题,说单行数据有个字段最大16k,对MySQL查询性能的影响有多大,是否会导致 MySQL下降。

数据行格式:

那是不是说,只要 varchar 中存储的数据大于 768 个字节,多余的字节就一定会存储的未压缩的二进制大对象页中?如果是这样的话,就会导致 B+ 树失去了意义(因为只有一个数据页,剩下数据就往二进制大对象页中插入就行),成为了一个链表。

如果一个数据页中恰好能放下两条记录,那么就不会把数据放到未压缩的二进制大对象页中去了,那么这个恰好放下两条记录的 varchar阈值 是多少呢?经过反复测试发现这个阈值为 8098 字节。

varchar 的存储方式如图所示

①读写场景(50%读50%写)

16K,对CPU压力较小,平均在20%

8K,CPU压力为30%~40%,但select吞吐量要高于16K

②读场景(100%读)

16K和8K差别不明显

InnoDB Buffer Pool管理页面本身也有代价,Page数越多,那么相同大小下,管理链表就越长。因此当我们的数据行本身就比较长(大块插入),更大的页面更有利于提升速度,因为一个页面可以放入更多的行,每个IO写的大小更大,可以更少的IOPS写更多的数据。 当行长超过8K的时候,如果是16K的页面,就会强制转换一些字符串类型为TEXT,把字符串主体转移到扩展页中,会导致读取列需要多一个IO,更大的页面也就支持了更大的行长,64K页面可以支持近似32K的行长而不用使用扩展页。 但是如果是短小行长的随机读取和写入,则不适合使用这么大的页面,这会导致IO效率下降,大IO只能读取到小部分。

2、关于Row_format

Innodb存储引擎保存记录,是以行的形式存放的。在InnoDB 1.0.x版本之前,InnoDB 存储引擎提供了 Compact 和 Redundant 两种格式来存放行记录数据。MySQL 5.1 中的innodb_plugin 引入了新的文件格式:Barracuda,该文件格式拥有新的两种行格式:compressed和dynamic。并且把 compact 和 redundant 合称为Antelope。可以通过命令SHOW TABLE STATUS LIKE ‘table_name’;来查看当前表使用的行格式,其中 row_format 列表示当前所使用的行记录结构类型。

MySQL 5.6 版本中,默认 Compact ,msyql 5.7.9 及以后版本,默认行格式由innodb_default_row_format变量决定,默认值是DYNAMIC,也可以在 create table 的时候指定ROW_FORMAT=DYNAMIC(通过这个可动态调整表的存储格式)。如果要修改现有表的行模式为compressed或dynamic,必须先将文件格式设置成Barracuda(set global innodb_file_format=Barracuda;)。再用ALTER TABLE tablename ROW_FORMAT=COMPRESSED;去修改才能生效,否则修改无效却无提示。

①compact

如果blob列值长度 768字节,那么前768字节依然在数据页,而剩余的则放在溢出页(off-page),如下图:

上面讲的blob或变长大字段类型包括blob、text、varchar,其中varchar列值长度大于某数N时也会存溢出页,在latin1字符集下N值可以这样计算:innodb的块大小默认为16kb,由于innodb存储引擎表为索引组织表,树底层的叶子节点为一双向链表,因此每个页中至少应该有两行记录,这就决定了innodb在存储一行数据的时候不能够超过8k,减去其它列值所占字节数,约等于N。

②compressed或dynamic

对blob采用完全行溢出,即聚集索引记录(数据页)只保留20字节的指针,指向真实存放它的溢出段地址:

dynamic行格式,列存储是否放到off-page页,主要取决于行大小,它会把行中最长的那一列放到off-page,直到数据页能存放下两行。TEXT/BLOB列 <=40 bytes 时总是存放于数据页。可以避免compact那样把太多的大列值放到 B-tree Node,因为dynamic格式认为,只要大列值有部分数据放在off-page,那把整个值放入都放入off-page更有效。

compressed 物理结构上与dynamic类似,但是对表的数据行使用zlib算法进行了压缩存储。在long blob列类型比较多的情况下用,可以降低off-page的使用,减少存储空间(50%左右,可参见之前“【数据库评测报告】第三期:innodb、tokudb压缩性能”报告中的测试结果),但要求更高的CPU,buffer pool里面可能会同时存储数据的压缩版和非压缩版,所以也多占用部分内存。

最后参考了《高性能MySQL》,给出一些使用BLOB这类变长大字段类型的建议:

①大字段在InnoDB里可能浪费大量空间。例如,若存储字段值只是比行的要求多了一个字节,也会使用整个页面来存储剩下的字节,浪费了页面的大部分空间。同样的,如果有一个值只是稍微超过了32个页的大小,实际上就需要使用96个页面。

②太长的值可能使得在查询中作为WHERE条件不能使用索引,因而执行很慢。在应用WHERE条件之前,MySQL需要把所有的列读出来,所以可能导致MySQL要求InnoDB读取很多扩展存储,然后检查WHERE条件,丢弃所有不需要的数据。

③一张表里有很多大字段,最好组合起来单独存到一个列里面。让所有的大字段共享一个扩展存储空间,比每个字段用自己的页要好。

④把大字段用COMPRESS()压缩后再存为BLOB,或者在发送到MySQL前在应用程序中进行压缩,可以获得显著的空间优势和性能收益。

⑤扩展存储禁用了自适应哈希,因为需要完整的比较列的整个长度,才能发现是不是正确的数据。

存储方式

对于不同行格式,有不同储存溢出页的方式

Compact & Reduntant

当发生行溢出时,在 Compact 和 Reduntant 中,’记录的真实数据’ 处只会存储一部分 (768 字节的) 数据,剩下的数据存储在几个其他的页 (溢出页) 中 (以链表的方式连接),在 ‘记录的真实数据’ 处用 20 个字节存储这些页的地址 (包含分散在其他页面中的数据的占用的字节数)

Dynamic & Compressed

它们不会在 ‘记录的真实数据’ 处储存真实数据的前 768 个字节,而是把所有的字节都存储到其他页面中,只储存其他页面的地址。

1、行数据格式:

你可以像下面这样看一下你的MySQL行格式设置。

root@test 11:44:17>show table status like 'tt1'\G;*************************** 1. row ***************************           Name: tt1         Engine: InnoDB        Version: 10     Row_format: Dynamic           Rows: 0 Avg_row_length: 0    Data_length: 16384Max_data_length: 0   Index_length: 0      Data_free: 0 Auto_increment: 2    Create_time: 2021-02-04 10:31:21    Update_time: NULL     Check_time: NULL      Collation: utf8_unicode_ci       Checksum: NULL Create_options:         Comment: 1 row in set (0.00 sec)

其实MySQL的数据行有两种格式,一种就是图中的 Compact格式,还有一种是Redundant格式。

Compact是一种紧凑的行格式,设计的初衷就是为了让一个数据页中可以存放更多的数据行。

MySQL以数据页为单位从磁盘中读数据,如果能做到让一个数据页中有更多的行,那岂不是使用的空间变少了,且交互变少了。

官网介绍:Compact能比Redundant格式节约20%的存储。Compact从MySQL5.0引入,MySQL5.1之后,行格式默认设置成 Compact 。

2、Compact行格式如下图:

3、MySQL单行能存多大

在MySQL的设定中,单行数据最大能存储65535byte的数据(注意是byte,而不是字符)

参考资料:

了解 MySQL的数据行、行溢出机制吗? – 赐我白日梦 – 博客园

https://blog.csdn.net/weixin_33273559/article/details/113141165

【MySQL InnoDB读书笔记】08 InnoDB下逻辑存储结构(一)_代码被吃掉了的博客-CSDN博客

SegmentFault 思否