在創建要給表的時候遇到一個有意思的問題,提示Specified key was too long; max key length is 767 bytes
,從描述上來看,是Key
太長,超過了指定的 767
字節限制。通常出現在嘗試創建一個過長的唯一鍵(UNIQUE KEY
)或主鍵(PRIMARY KEY
)時。MySQL對于InnoDB存儲引擎有一個索引鍵長度的限制,這個限制基于字符集的不同而不同。
下面是產生問題的表結構
CREATE TABLE `test_table` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(1000) NOT NULL DEFAULT '',`link` varchar(1000) NOT NULL DEFAULT '',PRIMARY KEY (`id`),KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
在使用utf8
字符集時,每個字符可能占用3
個字節,那么對于innodb
表,索引鍵的最大長度大約為1000
個字符左右(因為3072 / 3 ≈ 1024
)。若字符集是utf8mb4
,每個字符可能占用4
個字節,所以最大長度會進一步減少到768
個字符左右(3072 / 4 = 768
)
uf8mb4字符集:
要在 Mysql 中保存 4 字節長度的 UTF-8 字符,需要使用 utf8mb4 字符集(mb4就是most bytes 4的意思,專門用來兼容四字節的unicode),但只有 5.5.3 版本以后的才支持。
為了獲取更好的兼容性,應該總是使用 utf8mb4 而非 utf8. 對于 CHAR 類型數據,utf8mb4 會多消耗一些空間,根據 Mysql 官方建議,使用 VARCHAR 替代 CHAR。其實,utf8mb4是utf8的超集,理論上原來使用utf8,然后將字符集修改為utf8mb4,也會不會對已有的utf8編碼讀取產生任何問題。當然,為了節省空間一般情況下使用utf8也就夠了。
對于name,我們設置長度為1000
可變字符,因為采用utf8mb4
編碼, 所以它的大小就變成了 1000 * 4 > 767
所以再不修改其他配置的前提下,varchar的長度大小應該是 767 / 4 = 191
有興趣的同學可以測試下,分別指定name大小為191, 192
時,是不是前面的可以創建表成功,后面的創建表失敗,并提示錯誤Specified key was too long; max key length is 767 bytes
解決辦法
使用innodb
引擎
啟用innodb_large_prefix
選項,修改約束擴展至3072
字節
重新創建數據庫
my.cnf
配置
set global innodb_large_prefix=on;
set global innodb_file_per_table=on;
set global innodb_file_format=BARRACUDA;
set global innodb_file_format_max=BARRACUDA;
上面這個3072
字節的得出原因如下
我們知道InnoDB
一個page
的默認大小是16k
。由于是Btree組織,要求葉子節點上一個page
至少要包含兩條記錄(否則就退化鏈表了)。
所以一個記錄最多不能超過8k。又由于InnoDB
的聚簇索引結構,一個二級索引要包含主鍵索引,因此每個單個索引不能超過4k
(極端情況,pk和某個二級索引都達到這個限制)。
由于需要預留和輔助空間,扣掉后不能超過3500
,取個“整數”就是(1024*3
)。
在創建表的時候,加上 row_format=DYNAMIC
CREATE TABLE `test_table` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(255) NOT NULL DEFAULT '',`link` varchar(255) NOT NULL DEFAULT '',PRIMARY KEY (`id`),KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 row_format=DYNAMIC;
這個參數的作用如下
MySQL 索引只支持767
個字節,utf8mb4
每個字符占用4
個字節,所以索引最大長度只能為191
個字符,即varchar(191),若想要使用更大的字段,mysql需要設置成支持數據壓縮,并且修改表屬性 row_format ={DYNAMIC|COMPRESSED}