MySQL ERROR1118报错详解 Row size too large
ERROR1118的报错信息分为两种:
1、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
一行最大记录长度是65535(定义到这个长度也会报错,行本身维护也会占用字节),建议使用text或blobs类型。
2、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.
一条记录太长,超过了8126字节,建议部分列使用text或blob类型。
看到这两个报错信息,感觉描述的有些冲突,一个说一条记录最大长度 不超过65535字节,一个说长度不能超 过8126字节。
先看下官方文档的描述:
https://dev.mysql.com/doc/refman/5.7/en/column-count-limit.html#row-size-limits
【The MySQL maximum row size limit of 65,535 bytes is demonstrated in the following InnoDB
and MyISAM
examples. The limit is enforced regardless of storage engine, even though the storage engine may be capable of supporting larger rows.】
在MYSQL数据库中一条记录的最大长度是65535字节,以下以Innodb和mysiam存储引擎为例,做了相关演示,不管任何存储引擎,都不能超过这个范围,即存储引擎支持一行存储更长的数据。
【InnoDB
restricts row size (for data stored locally within the database page) to slightly less than half a database page for 4KB, 8KB, 16KB, and 32KB innodb_page_size
settings, and to slightly less than 16KB for 64KB pages.】
Inodb存储引擎,对于4K,8K,16K和32K的页面大小,限制一条记录最多使用半个页面,,64K页面比16KB页面限制稍小一些。
小结:
一条记录最大长度65535字节是MySQLO数据库Server层面的限制,
默认情况下,Innodb页面大小是16KB,所以
一条记录在页面中的存储长度不能超过8126字节,
一条记录在页面中的存储长度不能超过8126字节,
一条记录在页面中的存储长度不能超过8126字节,(重要的事情说三遍)
这是Innodb存储引擎的限制。
这里可能会有些疑问,平常创建varchar(10000)类型字段,已经超过8126了,但也没报这个错误,这个和Innodb的存储一条记录的格式有关系,
官方文档对存储格式的说明:
https://dev.mysql.com/doc/refman/5.7/en/innodb-row-format.html#innodb-row-format-compact
【Fixed-length columns greater than or equal to 768 bytes are encoded as variable-length columns, which can be stored off-page】
当列的长度超过768字节时,多余的内容会存储到一个溢出页上,compact/dynamic格式在这方面是一样的。
也就是说创建了varhcar(10000)类型字段,同时写入到10000字节的数据,其实只有768个字节存储在数据页面上,其余的字节存储在溢出页面上。
为什么Innodb存储引擎,每个存储页面上,最少要有两条记录,
截图来自于【MySQL运维内参】
这是假如每个页面只能存储一条记录的情况下,表内存储了【1,2,3,4】4条记录B+树结构图,
如果一个页面的数据量不能存储2条记录,则这个B+树就不能称为B+树,因为它起不到一个索引的作用,其实就是一个双向链表,但比双向链表占用的空间大很多。
如果不能够存储2条记录,那么这个B+树是没有意义的,形不成一个有效的索引。
总结:
创建表和写入数据时有两个限制,一个是Server层面的限制,一条记录最大长度不能超过65535(真实创建的记录长度到不了65535,因为记录本身也需要一些字节去维护)
另一个是Innodb层面的限制,一条记录存储在页在中的长度不能够超过8126字节。
实验:
1、创建一个表t,记录长度之合超过65535.默认字符集是latin1,一个字符占一个字节,如果用的utf8,则一个字符占用3个字节。要在定义的varchar字段类型上乘以3才是占用字节数。
看t2表,varchar类型是5000,记录最长是35000字节,没有达到server层面的限制,
每个字段的768字节存储在innodb页面上,其余的数据存储在溢出页面上。
t2表一共7个字段,每个字段只有前边的768字节存储在Innodb页面上,7*768=5376字节,没有达到Inodb存储引擎8126的限制,不会报错,所以创建成功。
mysql> CREATE TABLE t (a VARCHAR(10000), b VARCHAR(10000), -> c VARCHAR(10000), d VARCHAR(10000), e VARCHAR(10000), -> f VARCHAR(10000), g VARCHAR(6000)) ENGINE=InnoDB CHARACTER SET latin1; 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> create table t1(a varchar(10000),b varchar(10000),c varchar(2000)) character set utf8;
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> CREATE TABLE t2 (a VARCHAR(5000), b VARCHAR(5000),
-> c VARCHAR(5000), d VARCHAR(5000), e VARCHAR(5000),
-> f VARCHAR(5000), g VARCHAR(5000)) ENGINE=InnoDB CHARACTER SET latin1;
Query OK, 0 rows affected (0.01 sec)
2、创建t4表,使用char(255)定长字符串类型,char类型无论写入的内容多少(当然,一定要小于等于255),在实际存储时都会占用255个字节。
一共33字段,每个字段定长255字节,33*255=8415,每个记录最大长度是8145字节,是Server层的限制之内,所以没报65535的错误,
但一条记录在Innodb页面存储时超过了8126限制,所以Innodb存储引擎报错了。
mysql> CREATE TABLE t4 ( -> c1 CHAR(255),c2 CHAR(255),c3 CHAR(255), -> c4 CHAR(255),c5 CHAR(255),c6 CHAR(255), -> c7 CHAR(255),c8 CHAR(255),c9 CHAR(255), -> c10 CHAR(255),c11 CHAR(255),c12 CHAR(255), -> c13 CHAR(255),c14 CHAR(255),c15 CHAR(255), -> c16 CHAR(255),c17 CHAR(255),c18 CHAR(255), -> c19 CHAR(255),c20 CHAR(255),c21 CHAR(255), -> c22 CHAR(255),c23 CHAR(255),c24 CHAR(255), -> c25 CHAR(255),c26 CHAR(255),c27 CHAR(255), -> c28 CHAR(255),c29 CHAR(255),c30 CHAR(255), -> c31 CHAR(255),c32 CHAR(255),c33 CHAR(255) -> ) ENGINE=InnoDB ROW_FORMAT=COMPACT DEFAULT CHARSET latin1; ERROR 1118 (42000): 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.
3、修改t4表为varhcar(255),可变长字段试下,varchar字段类型在实际存储到页面的时候,并不以定长存储,而是写入多少字节,存储多少字节。
可以看到,这样创建表是没有问题,如果写入字节数小于8126字节也没有问题,
但是如果写入字节数超过8126了,由于Innodb存储引擎的限制,还是会报错的。
mysql> CREATE TABLE t4 ( -> c1 VARCHAR(255),c2 VARCHAR(255),c3 VARCHAR(255), -> c4 VARCHAR(255),c5 VARCHAR(255),c6 VARCHAR(255), -> c7 VARCHAR(255),c8 VARCHAR(255),c9 VARCHAR(255), -> c10 VARCHAR(255),c11 VARCHAR(255),c12 VARCHAR(255), -> c13 VARCHAR(255),c14 VARCHAR(255),c15 VARCHAR(255), -> c16 VARCHAR(255),c17 VARCHAR(255),c18 VARCHAR(255), -> c19 VARCHAR(255),c20 VARCHAR(255),c21 VARCHAR(255), -> c22 VARCHAR(255),c23 VARCHAR(255),c24 VARCHAR(255), -> c25 VARCHAR(255),c26 VARCHAR(255),c27 VARCHAR(255), -> c28 VARCHAR(255),c29 VARCHAR(255),c30 VARCHAR(255), -> c31 VARCHAR(255),c32 VARCHAR(255),c33 VARCHAR(255) -> ) ENGINE=InnoDB ROW_FORMAT=COMPACT DEFAULT CHARSET latin1; Query OK, 0 rows affected (0.01 sec)
测试1:写入数据长度小于8126的场景,240*33=7920,可以成功。
insert into t4 (c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,c21,c22,c23,c24,c25,c26,c27,c28,c29,c30,c31,c32,c33) values(repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240), repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240),repeat('a',240))
Query OK, 1 row affected (0.01 sec)
mysql>
场景2:写入数据长度大于8126的场景,255*33=8415,直接报错。
mysql> insert into t4 (c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,c21,c22,c23,c24,c25,c26,c27,c28,c29,c30,c31,c32,c33) -> values(repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255), -> repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255),repeat('a',255)); ERROR 1118 (42000): 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>