MySQL——MySQ行记录大小超过限制(Row size too large (> 8126). Changing some columns to TEXT or BLOB or ......)
背景Spark写MySQL,有太多列的长度都比较长MySQL中的可变长度类型variable-length typeA data type of variable length. VARCHAR, VARBINARY, and BLOB and TEXT types are variable-length types.InnoDB treats fixed-length fields greate
文章目录
问题背景
问题出现在Spark写MySQL的场景:要写入MySQL的DataFrame中有90多个列,其中有10多个列为字符串类型,且长度较长(大于1000);对应的要写入的MySQL表使用的是InnoDB引擎,这些较大的字符串所对应的列在MySQL中设置为text类型。
最终在写MySQL的时候,出现这样的报错:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline.
MySQL版本
本人使用的MySQL版本为5.6、InnoDB引擎,以下内容也是以这个前提来展开的,MyISAM暂不做介绍。
注:
- MySQL 5.6版本默认InnoDB文件格式为Antelope,相应配置innodb_file_format=Antelope,此种文件格式不支持COMPRESSED和DYNAMIC的行格式。
- MySQL 5.7版本默认InnoDB文件格式为Barracuda,相应配置innodb_file_format=Barracuda,此种文件格式支持COMPRESSED和DYNAMIC的行格式。
名词术语
要理解上面报错的本质,首先要了解以下概念:
- MySQL中的可变长度类型
- 页(page)、页大小(page size)、off-page column、overflow page?
- 页(Page)、行大小、行格式三种之间的关系
1. 可变长度类型
MySQL中的可变长度类型:VARCHAR、VARBINARY、BLOB和TEXT类型。
InnoDB将长度大于等于768字节的fixed-length字段当作可变长度字段,可以存储在off-page。
2. 页(page)、页大小(Page size)、off-page column、overflow page
i. 页(Page)
page代表InnoDB每次在磁盘和内存之间传输多少数据的一个单元。一个page可以包含一行或多行数据,这主要取决于每行数据的大小。如果一行记录不能全部放入到一个page中,InnoDB会用一个指针来引用这行数据。
可以使用COMPRESSED格式来使每个page容纳更多的数据。对于blob或者text类型的字段,COMPACT格式允许大长度的列和其他列分开存储,以便减少查询时的I/O负载和内存占用。
当InnoDB以批处理的方式读写一组page以增加I/O吞吐量时,它会一次读写一个区段的page。
ii. 页大小(Page size)
在MySQL 5.6版本之前,每个InnoDB page的大小都是固定的16KB,这是一个各方面取舍平衡的值:16KB能足以容纳大多数的行数据,同时也足够小到可以最小化将不必要的数据传输到内存的性能开销。
从MySQL 5.6开始,InnoDB page的大小可以是4KB、8KB或16KB,可通过innodb_page_size配置进行设置。在MySQL5.7.6中,InnoDB支持更大的page size(32KB和64KB),但是这两种page size并不支持ROW_FORMAT=COMPRESSED, 并且最大记录大小为16KB。
iii. off-page column
一个可变长度列(比如BLOB和VARCHAR)中的数据因为太大而不能放入一个B-tree page中,那么数据就会存储在overflow pages中。
iiii. overflow page
专门分配的磁盘pages,用来存储那些因为数据太长而不能放入B-tree page的可变长度列,这些可变长度列就是上面提到的off-page column。
3. 行格式
表的行格式决定了表中行是如何在物理层面上被存储的,这反过来又会影响增删查改操作的性能。当越多的行能被存储在单个page中时,那查询操作和索引的查找都会更高效,buffer pool就需要越少的缓存,更新操作就需要越少的I/O。
每个表中的数据都是被划分为很多个page的,这些page都是保存在B-tree这种数据结构中的,表中的数据和二级索引都是使用的这种数据结构。
长度较长的可变长度列由于无法存储到单个B-tree page中,只能存储到单独分配的磁盘页(overflow pagess)上。这些列也被称为off-page column。off-page columns的值存储在overflow pages的单链表中,而且每一列都有自己的列表,从这个列表中可以知道这一列的值都存储在哪些overflow page中。根据列长度的不同,会将变长列的全部值或前缀存储在B-tree中,这样就能避免page的浪费,也避免了要读取多个page的情况。
MySQL中常用的InnoDB引擎支持4中行格式:
- REDUNDANT
- COMPACT
- DYNAMIC
- COMPRESSED
更多关于InnoDB Row Formats的细节,参考这里。
4. 页(Page)、行大小、行格式三种之间的关系
MySQL表中行的最大长度被限制为65535字节,即使使用的存储引擎能够支持更大的行,也不能超过这个限制。
表中行的最大长度略少于数据库page大小的一半,例如,对于默认的InnoDB page大小16KB,所对应的行最大长度为略小于8KB,这个值是通过配置项innodb_page_size来设定的。
如果表中一行没有超过半个page的限制,那么整行数据都是存储在page中的;如果超过了半个page大小,那么对于可变长度列,超过限制的数据会被存储在外部off-page storage(就是上面提到的overflow page)。
而可变长度列是如何存储在off-page storage中的,又跟行格式的不同而不同:
- COMPACT 和 REDUNDANT行格式
在使用这两种行格式的情况下,当一个可变长度列被存储到外部的off-page storage中时,InnoDB引擎会把这一列的前768个字节存储在page中,剩下的数据存储在overflow pages中。每一个存储在overflow pages中的可变长度列都有一个自己的overflow pages列表。这768个字节中,有20字节用来存储这个列的真实长度和指向包含指向overflow list的指针。 - DYNAMIC和COMPRESSED行格式
在使用这两种行格式的情况下,当一个可变长度列被存储到外部的off-page storage中时,InnoDB引擎会在page中存储一个20字节的指针,列中的剩余数据会全部存储到overflow pages中。
解决方案
在使用InnoDB建表时,默认的行格式为COMPACT(可通过show variables like "table_name"查看),这种行格式对应的默认page大小为16KB,那么相应每行的大小不能超过8KB。如果表中有20个列都为text类型,而且每个text类型列的值都超过了768字节,那么20 * 768字节=15360字节=15KB远大于8KB,所以必然会报错!
那么解决这个问题的方法就是修改行格式,以下是启用DYNAMIC行格式的步骤:
-
首先是MySQL配置文件my.cnf中添加两个配置项:
innodb_file_per_table=1 // innodb_file_format = Barracuda //DYNAMIC行格式只有在Barracuda文件格式下才支持
-
修改表行格式ROW_FORMAT
alter table table_name ROW_FORMAT=DYNAMIC;
修改之后,执行 show table status like ‘table_name’,可以看到Row_format这一列对应的值已经变成了dynamic,再写入数据的时候就不会报错了。
参考
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)