今天被開發提交的DDL變更再次困惑,表中字段較多,希望將已有的兩個varchar(4000)字段改為varchar(20000),我想innodb對varchar的存儲不就是取前768字節記錄當前行空間嘛,所以變更不會有任何問題的,但鐵打的事實給了我結結實實的一個巴掌,直接報錯,現在回放下這個錯誤!
模擬測試:
CREATE TABLE `ttt` (
`id` DOUBLE ,
`select_type` VARCHAR (57),
`table` VARCHAR (192),
`type` VARCHAR (30),
`possible_keys` VARCHAR (22288),
`key` VARCHAR (192),
`key_len` VARCHAR (22288),
`ref` VARCHAR (3072),
`rows` DOUBLE ,
`Extra` VARCHAR (765)
);
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
看了提示,表的2個varchar字段長度設置過長了,需要改成text,blob之類的類型,修改之后執行成功了。
mysql> use test;
Database changed
mysql>
mysql> CREATE TABLE `ttt` (
-> `id` DOUBLE ,
-> `select_type` VARCHAR (57),
-> `table` VARCHAR (192),
-> `type` VARCHAR (30),
-> `possible_keys` TEXT,
-> `key` VARCHAR (192),
-> `key_len` TEXT,
-> `ref` VARCHAR (3072),
-> `rows` DOUBLE ,
-> `Extra` VARCHAR (765)
-> );
Query OK, 0 rows affected (0.00 sec)
疑惑:varchar(N),這個N不是最大為65535嗎?為什么設置成12288就會報錯?12288比65535小很多啊。
上官網:http://dev.mysql.com/doc/refman/5.6/en/column-count-limit.html:
Every table (regardless of storage engine) has a maximum row size of 65,535 bytes. Storage engines may place additional constraints on this limit, reducing the effective maximum row size.
解析:65,535所說明的是針對的是整個表的非大字段類型的字段的bytes總合。
欲看詳細分析還要繼續往下看
每個表有4096個列的硬性限制,但是到具體表是往往小於這個數字,確切的限制取決於幾個相互作用的因素:
-
每個表(不考慮存儲引擎)為65,535字節的最大行大小限制。存儲引擎可能會對這個限制進行額外的限制,降低了有效的最大行大小。
- 受到行大小限制,列的數目還要看具體的字段長度,例如,UTF8字符需要三個字節存儲,因此對於CHAR(255)CHARACTER SET UTF8列,服務器必須分配255×3 =765的字節。因此,一個表不能包含超過65,535/765=85這樣的列。
- 可變長度列在評估字段大小時還要考慮存儲列實際長度的字節數。例如,VARCHAR(255)CHARACTER SET UTF8列需要額外的兩個字節來存儲值長度信息,所以該列需要多達767個字節存儲,其實最大可以存儲65533字節,剩余兩個字節存儲長度信息。
- BLOB和TEXT列不同於varchar字段,列長度信息獨立於行長存儲,可以達到65535字節真實存儲。
- 聲明NULL列可降低允許的最大列數。對於MyISAM表,NULL列需要該行中額外的空間記錄其值是否為NULL。每個NULL列需要一個額外的位,四舍五入到最接近的字節。
最大行長度計算如下: row length = 1 + (sum of column lengths) + (number of NULL columns + delete_flag + 7)/8 + (number of variable-length columns)
對於靜態表,delete_flag = 1,靜態表通過在該行記錄一個位來標識該行是否已被刪除。 動態表時delete_flag = 0,因為該標記存儲在動態行首,動態表具體可以根據row_format判斷,詳情參考Section 15.2.3, “MyISAM Table Storage Formats”
對於InnoDB表,NULL和NOT NULL列存儲大小是一樣,因此上述計算並不適用。
以下測試是沒有問題的,(32765 + 2 + 32766 + 2 )bytes < 655535
mysql> CREATE TABLE t1 -> (c1 VARCHAR(32765) NOT NULL, c2 VARCHAR(32766) NOT NULL) -> ENGINE = MyISAM CHARACTER SET latin1; Query OK, 0 rows affected (0.02 sec)
因為NULL屬性需要額外的儲存空間,超過了最大65535的限制
mysql> CREATE TABLE t2 -> (c1 VARCHAR(32765) NULL, c2 VARCHAR(32766) NULL) -> ENGINE = MyISAM CHARACTER SET latin1; ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs
因為變成字段需要占用行內額外的儲存空間,所以超過了最大65535的限制
mysql> CREATE TABLE t3 -> (c1 VARCHAR(65535) NOT NULL) -> ENGINE = MyISAM CHARACTER SET latin1; ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs
-
除此之外,一些存儲引擎可能會強加限制表中列數的附加限制。如:
- InnoDB允許單表最多1000個列
- InnoDB限制行大小不到數據庫頁面的一半,不包括VARBINARY,VARCHAR,BLOB或TEXT列。欲了解更多信息,更多影響體現DML,而不是DML。詳情參考Limits on InnoDB Tables
- InnoDB不同的存儲格式(壓縮,冗余)使用不同數量的頁面頭部和尾部,這會影響可用於存儲行的長度。
-
如果innodbstrictmode禁用,創建表時使用冗余(REDUNDANT)或緊湊COMPACT格式,如果列超過最大行大小也會成功的定義,只是產生警告:
| Warning | 139 | Row size too large (> 8123). 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.
-
如果innodbstrictmode禁用,創建表時使用動態(DYNAMIC)或壓縮(COMPRESSED)格式,如果列超過最大行大小也會成功的定義,就會直接報錯:
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.
-
每個表都有一個包含表定義的.frm文件。文件定義的內容也會影響到字段數的上限,詳情參考Limits Imposed by .frm File Structure
參考: