数据库

MySQL报错:ERROR 1118 (42000): Row size too large. 或者 Row size too large (> 8126).

2024-05-23

用sql文件建表时,提示错误:

ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs.


报错原因:


MySQL 建表时有一个单行最大限制长度限定:一张表中所有字段设置的字节数总和不大于 65535 字节。


注意点一:单个字段大小超过 65535 转换为 TEXT。

注意点二:其余字段总和不超过 65535 字节(不包括 BLOB/TEXT)。

注意点三:数据库使用 UTF-8 编码,一个字符 = 三个字节大小(使用编码不同,字节数大小略有不同)。

报错举例:数据库存在 10 个 varchar 字段,每个大小为 3000 则数据库单行目前计算长度为 3000 * 10 * 3 = 90000 > 65535 ,则建表时就会报错。


解决办法: 将数据库表大字段类型设置为 TEXT,或者将部分可以减小长度的长度调小至总和小于 65535。


调整完之后,执行建表语句,又报如下错误:

SQL错误(1118):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.


1. 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.(低版本报错)


2. Row size too large (> 8126). Changing some columns to TEXT or BLOB may help. In current row format, BLOB prefix of 0 bytes is stored inline. (新版本报错)


上面两个错误信息,前者基本出现在 ROW_FORMAT <> DYNAMIC 或者较早版本的情况,在其中 BLOB 需要存储 768 字节在行内部。对整个行 size 贡献较大。


后者基本对于较新的版本,因为默认的 ROW_FORMAT = DYNAMIC,在其中,一个 TEXT 或者 BLOB 字段对行 size 的贡献在 9-12 个字节之间。


对于第一种情况,可以设置 innodb_file_per_table = 1,innodb_default_row_format = dynamic,又因为 dynamic 要求 innodb_file_format 必须为 Barracuda,所以一般还要加上 innodb_file_format = Barracuda 设置。innodb_default_row_format = dynamic 可以在创建表的时候动态指定。当然也可以按照提示那样的,设置 ROW_FORMAT =COMPRESSED,这个对于只读场景用处比较大,如果用于读写负载,那比较不好。


在数据库执行如下语句:

SHOW GLOBAL VARIABLES LIKE '%innodb_file%';


| Variable_name            | Value     |

+--------------------------+-----------+

| innodb_file_format       | Barracuda |

| innodb_file_format_check | ON        |

| innodb_file_format_max   | Barracuda |

| innodb_file_per_table       | ON        


确保 innodb_file_format 使用的是 Barracuda,innodb_file_per_table 使用的是 ON,如果不是执行以下语句(不用重启 MySQL):

SET GLOBAL innodb_file_format = barracuda;

SET GLOBAL innodb_file_per_table = ON;


或者,在配置文件中添加使用独立表空间的配置:innodb_file_per_table=1

修改配置文件 my.cnf(需要重启 MySQL):

innodb_file_per_table = ON;

innodb_file_format = barracuda;


或者,建表语句设置 ROW_FORMAT =COMPRESSED:

create table_name (

...

)

ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FROMAT=COMPRESSED COMMENT='表注释';



如果上面的方法仍然解决不了问题,那还有其它办法:


1、关掉 innodb_strict_mode,这个选项是在创建表的时候检查行大小,如果确定实际存储的字段没有这么多,可以关掉。但是问题是如果确实有这么多内容,插入的时候会报错。

查看:


SHOW VARIABLES LIKE '%innodb_strict_mode%';


SHOW VARIABLES LIKE '%innodb_strict_mode%';

Variable_name           Value     

innodb_strict_mode     ON        |


修改:

SET SESSION innodb_strict_mode = OFF

或者

SET innodb_strict_mode = OFF


以上 OFF 也可以用 0 代替,ON 也可以用 1 代替 。


2、将 innodb_page_size 调整成 64K,这样,64K 的 page 即使需要容纳2行数据的话,每行也可以最大达到 32K(实际达不到,因为 header 和 footer 需要空间)。但是这个最好把现有的 MySQL 备份出来,然后按照新的 page size 重新初始化,再导入备份,保证整个库都使用统一的 page size 大小,以免出现稀奇古怪的问题。

set global innodb_page_size = 65536


3、可以看到上面的提示 In current row format, BLOB prefix of 0 bytes is stored inline,把较长的字段都转成 TEXT 或者 BLOB 存储。


以上步骤可以一个一个试,基本就可以解决自己的问题了。