MySQL 不同字符集列的索引长度参数对比
来源:17golang原创
时间:2026-10-31 23:30:24 118浏览 收藏
这类问题通常出现在把旧表从 utf8mb3 迁移到 utf8mb4 之后:原来能创建的 INDEX(name(255)) 突然报索引过长。根因不是“255 这个参数失效”,而是 MySQL 对非二进制字符串把前缀参数按字符数解释,却按字节数检查索引上限。utf8mb4 最多使用 4 字节表示一个字符,所以同样的 255 个字符,最坏情况会占 1020 字节。
官方文档:https://dev.mysql.com/doc/refman/8.4/en/column-indexes.html
- 非二进制字符串的
INDEX(col(N))中,N按字符数写;索引前缀上限按字节数算。 - 保守换算公式是:安全字符数 = floor(字节上限 ÷ 字符集最大字节数)。
- 迁移前要同时看字符集、InnoDB 行格式、联合索引的总和与前缀选择性,不能只把 255 改成 191。
影响面:同一个 255 参数为什么在迁移后失败
我在排查这类迁移错误时,先把问题拆成两个单位:DDL 中的长度是字符,存储引擎限制是字节。MySQL 文档明确说明,CHAR、VARCHAR、TEXT 的索引前缀参数按字符解释;BINARY、VARBINARY、BLOB 则按字节解释。
因此旧表若使用 utf8mb3,255 个字符按最坏情况估算为 255 × 3 = 765 字节,可以贴近 767 字节上限。转换为 utf8mb4 后,最坏情况变为 255 × 4 = 1020 字节;在 COMPACT 或 REDUNDANT 行格式下就可能触发 Specified key was too long。

时间线:先按字符集换算,再对照两种常见上限
可以用下面这张保守对照表快速判断。它使用字符集的最大字节数,而不是某一批实际数据的平均字节数;这样迁移时不会因为当前样本 mostly 是 ASCII 而低估风险。
| 字符集 | 最大字节/字符 | 767 字节上限的保守前缀 | 3072 字节上限的保守前缀 |
|---|---|---|---|
| ascii / latin1 | 1 | 767 字符 | 3072 字符 |
| utf8mb3 | 3 | 255 字符 | 1024 字符 |
| utf8mb4 | 4 | 191 字符 | 768 字符 |
| utf16 | 最多 4 | 按 4 字节保守估算 191 字符 | 按 4 字节保守估算 768 字符 |
767 字节适用于 InnoDB 的 COMPACT 或 REDUNDANT 行格式;DYNAMIC 或 COMPRESSED 可使用到 3072 字节的前缀上限。这里的“安全前缀”是最坏字节数下的整数下取整,不代表前缀越长查询就一定越快。
-- 用表定义确认列字符集和行格式,再决定迁移参数。 SHOW CREATE TABLE customer; -- 查看索引是否已经是前缀索引,Sub_part 对字符串表示字符数。 SHOW INDEX FROM customer; -- 用最坏字节数做迁移前的保守估算。 -- utf8mb4 在 767 字节上限下:FLOOR(767 / 4) = 191。
触发条件:单列能算对,联合索引仍可能超限
迁移中第二个容易漏掉的点是联合索引。比如 (title(191), slug(191)) 的两个前缀都分别符合 utf8mb4 的 767 字节单列换算,但它们放在同一个索引键中时,必须按各 key part 的最坏字节数合计,再结合该存储引擎的总键长度限制判断。列字符集不同,也要分别使用各自的最大字节数。
另外,VARCHAR(255) 的列长度和 INDEX(col(191)) 的索引前缀不是同一个参数:前者决定列最多存多少字符,后者只把前 N 个字符放进索引。把列直接改成 VARCHAR(191) 会改变数据模型,只有确认业务不需要更长值时才这样做;仅为了降低索引大小,优先讨论索引前缀。

根因修复:缩短前缀、调整行格式,还是重新评估选择性
如果目标表确实是旧行格式,且业务只需要前缀查找,最小改动通常是把 utf8mb4 的前缀从 255 调整为 191,再单独评估唯一索引是否仍能满足唯一性。唯一索引不能因为超限而悄悄缩短,否则可能把原本不同的值压成相同的索引键。
如果表可以采用 DYNAMIC 或 COMPRESSED 行格式,则可以使用更大的 3072 字节预算,但这仍不是“所有索引都写 768”的理由:联合索引、实例页大小和列的实际业务区分度都需要重新核对。前缀太短会降低选择性,让 MySQL 在索引筛选后回表检查更多记录。
-- 仅示意旧表迁移;先在变更窗口确认行格式和索引用途。 ALTER TABLE customer MODIFY title VARCHAR(255) CHARACTER SET utf8mb4 NOT NULL, DROP INDEX idx_title, ADD INDEX idx_title (title(191)); -- 调整后再次查看索引定义,确认前缀参数已经落在目标列上。 SHOW INDEX FROM customer WHERE Key_name = 'idx_title';
防复发:把“字符参数”与“字节预算”写进迁移清单
- 先读
SHOW CREATE TABLE,记录每个索引列的字符集、排序规则和 InnoDB 行格式。 - 按最大字节数计算,而不是拿现有数据样本的平均长度计算。
- 联合索引按所有 key part 的最坏情况汇总;涉及唯一索引时,先确认缩短前缀不会破坏唯一性。
- 前缀缩短后关注选择性;必要时改为更适合查询的列组合,而不是无条件放大索引。
常见问题
为什么 utf8mb4 常见的前缀长度是 191?
在 767 字节的旧 InnoDB 前缀上限下,按每字符最多 4 字节保守计算,floor(767 / 4) = 191。这不是 utf8mb4 的固定语法限制,而是字节上限换算结果。
实际数据大多是英文,可以按 1 字节计算吗?
不建议用于结构迁移。索引定义要能容纳列允许的最坏字符,实际值偏 ASCII 只能帮助理解平均占用,不能替代上限计算。
把行格式改成 DYNAMIC 后就不用管前缀了吗?
不用。它只把某些 InnoDB 表的前缀预算提高到 3072 字节,联合索引合计、页大小、唯一性和前缀选择性仍然要单独判断。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
306 收藏
-
271 收藏
-
368 收藏
-
335 收藏
-
175 收藏
-
374 收藏
-
154 收藏
-
432 收藏
-
232 收藏
-
287 收藏
-
233 收藏
-
417 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习