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>