序文
最近、いくつかのテーブルを移行しましたが、767 例外が発生しました。移行前は sql_mode のみをチェックし、このパラメータを無視していました。その結果、いくつかのテーブルの作成に失敗しました。実際、解決策も非常に簡単です。パラメータを有効にして、逆輸入しますinnodb_large_prefix
。このエラーは、文字列型の大きなフィールドでよく発生します。この記事では、この問題を簡単に記録します。
[42000][1071] 指定されたキーが長すぎます。キーの最大長は 767 バイトです
1. 767バイトの解釈
特定のテーブルの文字列フィールドが何バイトを占めているかを知るにはどうすればよいでしょうか? テーブルの文字セットと併せて確認する必要があります。
CREATE TABLE `test_index` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(800) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
上の表の文字タイプは utf8 です。表を参照することで、文字が占有できる最大スペースを決定できます。
show character set;
文字コード | 説明 | デフォルトの照合順序 | マックスレン |
---|---|---|---|
ビッグ5 | Big5 繁体字中国語 | big5_chinese_ci | 2 |
12月8日 | 12 月 西ヨーロッパ | dec8_スウェーデン語_ci | 1 |
CP850 | DOS 西ヨーロッパ語 | cp850_general_ci | 1 |
hp8 | HP西ヨーロッパ | hp8_english_ci | 1 |
こい8r | KOI8-R Relcom ロシア語 | koi8r_general_ci | 1 |
ラテン語1 | cp1252 西ヨーロッパ人 | latin1_スウェーデン語_ci | 1 |
ラテン語2 | ISO 8859-2 中央ヨーロッパ | latin2_general_ci | 1 |
swe7 | 7ビットスウェーデン語 | swe7_スウェーデン_ci | 1 |
アスキー | US ASCII | ascii_general_ci | 1 |
テスト | EUC-JP 日本語 | ujis_japanese_ci | 3 |
たわごと | シフトJIS日本語 | sjis_japanese_ci | 2 |
ヘブライ語 | ISO 8859-8 ヘブライ語 | hebrew_general_ci | 1 |
tis620 | TIS620 タイ語 | tis620_thai_ci | 1 |
ユークル | EUC-KR 韓国語 | euckr_korean_ci | 2 |
koi8u | KOI8-U ウクライナ語 | koi8u_general_ci | 1 |
GB2312 | GB2312 簡体字中国語 | gb2312_中国語_ci | 2 |
ギリシャ語 | ISO 8859-7 ギリシャ語 | greek_general_ci | 1 |
CP1250 | Windows 中央ヨーロッパ | cp1250_general_ci | 1 |
gbk | GBK 簡体字中国語 | gbk_chinese_ci | 2 |
ラテン5 | ISO 8859-9 トルコ語 | latin5_turkish_ci | 1 |
armcii8 | ARMSCII-8 アルメニア語 | armcii8_general_ci | 1 |
utf8 | UTF-8 ユニコード | utf8_general_ci | 3 |
ucs2 | UCS-2 ユニコード | ucs2_general_ci | 2 |
CP866 | DOSロシア語 | cp866_general_ci | 1 |
keybc2 | DOS カメニッキー チェコ語 - スロバキア語 | keybcs2_general_ci | 1 |
死んだ | マック セントラル ヨーロッパ | macce_general_ci | 1 |
マクロマン | マックウエストヨーロピアン | マクロマン_ジェネラル_ci | 1 |
CP852 | DOS 中央ヨーロッパ | cp852_general_ci | 1 |
ラテン7 | ISO 8859-13 バルト海 | latin7_general_ci | 1 |
utf8mb4 | UTF-8 ユニコード | utf8mb4_general_ci | 4 |
CP1251 | Windows キリル文字 | cp1251_general_ci | 1 |
edf16 | UTF-16 ユニコード | utf16_general_ci | 4 |
utf16le | UTF-16LE ユニコード | utf16le_general_ci | 4 |
CP1256 | Windows アラビア語 | cp1256_general_ci | 1 |
cp1257 | Windows Baltic | cp1257_general_ci | 1 |
utf32 | UTF-32 Unicode | utf32_general_ci | 4 |
binary | Binary pseudo charset | binary | 1 |
geostd8 | GEOSTD8 Georgian | geostd8_general_ci | 1 |
cp932 | SJIS for Windows Japanese | cp932_japanese_ci | 2 |
eucjpms | UJIS for Windows Japanese | eucjpms_japanese_ci | 3 |
gb18030 | China National Standard GB18030 | gb18030_chinese_ci | 4 |
例如 utf8 的 Maxlen = 3 表示一个字符最大占用 3 bytes。那么 varchar 类型小于 255 字符,就不会触发 767 异常。
2. 异常原因
为什么会有这个异常呢?其实该异常属于 MySQL Innodb 引擎的限制,例如一张表最多有 1017 个字段,一张表最大有 64 个二级索引一样,如果索引列的字符长度很大的话,二级索引占用的空间也会非常大,一个页面存储的索引字段减少,索引深度会更容易变深,增加 IO 次数,搜索的性能也不会理想。
If innodb_large_prefix is enabled (the default), the index key prefix limit is 3072 bytes for InnoDB tables that use the DYNAMIC or COMPRESSED row format. If innodb_large_prefix is disabled, the index key prefix limit is 767 bytes for tables of any row format.
Innodb 引擎的限制:14.23 InnoDB Limits
3. 5.7 和 8.0 版本的区别
在 5.7 版本中,对于使用 DYNAMIC 或 COMPRESSED 行格式的 InnoDB 表,索引前缀的限制为 767 bytes,如果开启 innodb_large_prefix 参数,限制阈值上调为 3072 bytes。
在 8.0 版本中,参数 innodb_large_prefix 被删除,索引前缀最大为 3072 bytes 不再有 767 bytes 的限制。
ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes
4. 版本 5.6 特殊情况
在 5.6 版本中 innodb_default_row_format = Compact 默认值,只有 DYNAMIC 和 COMPRESSED 行格式突破 767,所以 5.6 版本修改参数后,依然无法创建大于 767 字节的索引,此时可以去查表的行格式,大概率是行格式不支持:
select TABLE_SCHEMA, TABLE_NAME, ROW_FORMAT
from information_schema.TABLES where TABLE_NAME = '表名'
此时修改行格式可以解决:
alter table 表名 row_format=dynamic;