根本原因是索引键字节数超限,而非字段长度本身;需确保索引键≤当前innodb实际允许的最大字节数(767/3072),关键取决于row_format与innodb_large_prefix是否同时满足生效条件。

直接结论:不是字段太长,而是索引键计算字节数超了;根本解法是让索引键 ≤ 当前 InnoDB 实际允许的最大字节数(767 / 1071 / 3072),而不是盲目改字段长度。
为什么 ALTER TABLE t ADD INDEX idx_token(token) 会报 1071 错误?
错误信息 Specified key was too long; max key length is 1071 bytes 不是“字段不能那么长”,而是 MySQL 按字符集把字段长度换算成字节后超限。比如:
-
token是VARCHAR(255),字符集为utf8mb4→ 单字符最多 4 字节 → 理论索引长度 =255 × 4 = 1020字节 - 1020 已逼近默认上限 1071,但若表
ROW_FORMAT是COMPACT或REDUNDANT,实际仍卡在 767 字节 - 联合索引更危险:
(email(191), name(191))在utf8mb4下就是191×4×2 = 1528字节,必然失败
怎么查当前表的索引字节限制和真实瓶颈?
别猜,用 SQL 直接看:
SHOW VARIABLES LIKE 'innodb_large_prefix';
SHOW TABLE STATUS LIKE 't'; -- 关注 Row_format 字段
再查已有索引实际占用字节数:
SELECT
table_name,
index_name,
SUM(
CASE
WHEN character_set_name = 'utf8mb4' THEN 4 * IFNULL(sub_part, column_length)
WHEN character_set_name = 'utf8' THEN 3 * IFNULL(sub_part, column_length)
ELSE IFNULL(sub_part, column_length)
END
) AS total_bytes
FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 't'
GROUP BY table_name, index_name;
关键判断点:
- 若
Row_format是COMPACT或REDUNDANT→ 实际上限就是 767 字节,innodb_large_prefix开了也无效 - 若
Row_format是DYNAMIC或COMPRESSED,且innodb_large_prefix=ON→ 上限才可能是 3072 字节(MySQL 5.7.7+ 默认)
建前缀索引时,token(191) 为什么查不到数据?
前缀索引不是模糊匹配工具,它只加速严格左前缀查询。常见失效场景:
-
WHERE token = 'xxx'✅ 可用 —— 前提是完整值以该前缀开头(MySQL 内部做等值比对) -
WHERE token LIKE 'abc%'✅ 可用 —— 必须左通配,且'abc'长度 ≤ 前缀长度(如token(64)就不能匹配LIKE 'abcd...'超过 64 的模式) -
WHERE token LIKE '%abc'❌ 全表扫描 —— 右模糊无法利用 B+ 树结构 -
ORDER BY token❌ 退化为filesort—— 前缀索引不提供完整排序能力 -
SELECT token FROM t WHERE token = 'x' GROUP BY token❌ 无法覆盖 —— 前缀索引不含完整字段值,不能用于覆盖索引
比前缀索引更稳的替代方案有哪些?
前缀索引是妥协方案,真正要兼顾性能、准确性和可维护性,优先考虑这些:
-
哈希转换:存
SHA256(token)到CHAR(64)字段,建唯一索引 —— 固定长度、无选择性衰减、支持等值查,但无法支持LIKE或范围查询 -
分表/关联表:如 JWT 黑名单场景,拆出
token_blacklist(token_hash, expires_at),主表只存业务字段 —— 彻底规避长字段索引问题,也利于 TTL 清理 - 应用层截断 + 校验:仅当字段语义允许(如 URL 域名部分),取前 N 位建索引,同时业务代码校验完整值一致性 —— 风险高,需强约定
真正容易被忽略的是:innodb_large_prefix 和 ROW_FORMAT 必须同时生效,缺一不可;而 RDS 类托管服务往往不开放 SET GLOBAL,必须通过控制台修改参数并重建表 —— 这一步漏掉,所有 token(N) 都白调。











