mysql 8.0默认utf8mb4导致“specified key was too long”错误,根本原因是单字符占4字节使varchar(255)达1020字节,超出innodb默认767字节限制;虽上限升至3072字节,但需同时满足row_format=dynamic和innodb_file_per_table=on,否则仍按767字节校验。

为什么utf8mb4会让索引报“Specified key was too long”
不是MySQL 8.0改了规则,而是utf8mb4单字符最多占4字节,而InnoDB索引键长度限制没自动跟着翻倍。老表用VARCHAR(255)配utf8(实际是utf8mb3)时,255 × 3 = 765字节,刚好卡在默认767字节上限内;换成utf8mb4后,255 × 4 = 1020字节,直接超限,建索引或ALTER TABLE就会触发ERROR 1071 (42000)。
MySQL 8.0的3072字节上限为什么没生效
因为3072字节支持有两个硬性前提,缺一不可:
-
ROW_FORMAT = DYNAMIC(或COMPRESSED),不能是COMPACT或REDUNDANT -
innodb_file_per_table = ON(8.0默认开,但升级的老实例可能仍沿用共享表空间)
即使SHOW VARIABLES LIKE 'innodb_large_prefix'返回空(8.0已废弃该参数),只要SHOW CREATE TABLE t里显示ROW_FORMAT=COMPACT,就仍在走767字节逻辑——这是最常被忽略的静默失效点。
建表或迁移时必须显式控制的三件事
别依赖“默认已好”,尤其从5.7升级或容器部署:
- 建表语句里加
ROW_FORMAT=DYNAMIC,例如:CREATE TABLE t (...) ROW_FORMAT=DYNAMIC; - 字段定义长度要重算:utf8mb4下安全全字段索引上限是
VARCHAR(191)(191 × 4 = 764 ≤ 767),不是255 - 联合索引更敏感:比如
UNIQUE KEY uk(a, b),若a是VARCHAR(100)、b是VARCHAR(100),总长可能达800+字节,必须加前缀或精简
ALTER TABLE CONVERT TO CHARACTER SET utf8mb4为什么会卡住
这不是字符集转换本身慢,而是条件不满足时MySQL会静默退回到COPY算法:
- 持有SX元数据锁,阻塞所有DML,大表可能卡数分钟
- 旧表若仍是
ROW_FORMAT=COMPACT,CONVERT TO不会自动升级行格式,索引仍按767字节校验,失败后也不报错提示 - 正确做法是分步:先
ALTER TABLE t ROW_FORMAT = DYNAMIC;,再CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
真正容易被忽略的是连接层:客户端没设charset=utf8mb4,会导致隐式转换、索引失效、锁范围扩大——这比索引超长更难排查。











