问题现象
客户开发创建表时,报错
ERROR 1118: 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.
问题分析
MySQL创建字段的所有字段长度上限
innodb_strict_mode = on
innodb_strict_mode启用时, 在InnoDB检查无效或不兼容的表选项时返回错误而不是警告。
它检查 、KEY_BLOCK_SIZE、 ROW_FORMAT、DATA DIRECTORY和TEMPORARY选项 TABLESPACE是否相互兼容以及是否与其他设置兼容。
innodb_strict_mode=ON还可以在创建或更改表时启用行大小检查,以防止 INSERT或UPDATE由于记录对于所选页面大小来说太大而失败。
innodb_strict_mode您可以在启动时在命令行上mysqld或在 MySQL配置文件 中启用或禁用 。您还可以 innodb_strict_mode在运行时使用语句启用或禁用
InnoDB引擎限制
ROW_FORMAT=DYNAMIC
一个表最多可以包含 1017 列。虚拟生成的列包含在此限制中。
一个表最多可以包含 64 个 二级索引。
DYNAMIC行格式支持最多 3072 字节的索引键前缀
多列索引最多允许 16 列。超出限制会返回错误。
对于 4KB、8KB、16KB 和 32KB 页面大小,最大行大小(不包括存储在页外的任何可变长度列)略小于页面的一半。例如,默认 innodb_page_size16KB 的最大行大小约为 8000 字节。但是,对于InnoDB 64KB 的页面大小,最大行大小约为 16000 字节。LONGBLOB和 LONGTEXT 列必须小于 4GB,并且包括列在内的总行大小BLOB必须 TEXT小于 4GB。
如果一行的长度小于半页,则所有行都存储在页面的本地。如果它超过半页,则选择可变长度列用于外部页外存储,直到该行适合半页。
尽管InnoDB内部支持大于 65,535 字节的行大小,但 MySQL 本身对所有列的组合大小施加了 65,535 的行大小限制。
通过阅读上述的官方文档,我们可以得知:
1.数据库表使用INNODB引擎;
2.数据库8.0,innodb_strict_mode 参数启用ON;
3.数据库行存储格式,使用ROW_FORMAT=DYNAMIC;
4.数据库使用默认大小,页innodb_page_size 16k时;
5.那么Mysql在创建表的时候,就会检测ALL字段的行长度,不能大于1/2 pages,就是8k;
问题复现
create table cc_test (
id varchar(32) not null,
fid varchar(12) default null,
val_1 text default null,
val_2 text default null,
......
val_200 text default null
primary key (id),
key index_fid (fid) using btree
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ERROR 1118 (42000): 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.
方法一、网上大部分遇到的问题是varchar的字段加一起>8126,建议对varchar字段改成BLOB OR TEXT字段进行处理;
方法二、或者减少字段数量
模拟减少字段
省略,减少到196个text字段,创建成功,佐证了减少字段长度确实可以解决问题。
方法三、修改参数规避
innodb_strict_mode = off
会话层面修改:
mysql> create table cc_test (
-> id varchar(32) not null,
-> fid varchar(12) default null,
-> val_1 text default null,
.......
-> primary key (id)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected, 5 warnings (0.10 sec)
mysql> show warnings;
+---------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+---------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Warning | 1681 | Integer display width is deprecated and will be removed in a future release. |
| Warning | 1681 | Integer display width is deprecated and will be removed in a future release. |
| Warning | 1681 | Integer display width is deprecated and will be removed in a future release. |
| Warning | 3719 | 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. |
| Warning | 139 | 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. |
+---------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
5 rows in set (0.00 sec)
小结
1.虽然本次问题通过修改参数,临时规避了创建报错的问题;
2.但是Mysql启用check的目的是为了让表的查询、修改的效率得到提升,避免单行数据过大,但是本次的业务使用大量的text字段,里面存储的数据基本上是大于单行的长度,等于是可以存储数据,但是性能会很差;
参考
https://mysql.net.cn/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_strict_mode
https://mysql.net.cn/doc/refman/8.0/en/innodb-row-format.html




