MySQL utf8mb4 索引为什么超长:前缀索引、排序规则与唯一性取舍
来源:17golang原创
时间:2026-07-26 12:32:26 499浏览 收藏
老订单表把 email 定义成 VARCHAR(255),原来用 utf8 时唯一索引一直正常;切换到 utf8mb4 后,执行 ALTER TABLE 却收到“Specified key was too long”的错误。问题不在于邮箱突然变长,而在于索引长度按字符集的最大字节数计算。处理这类迁移时,最先要保住的是业务上的唯一性语义,而不是简单把索引前缀砍短。
索引超长报错不是字符数超了,是utf8mb4按单字符最大4字节折算后的总字节数触到引擎上限,优先保障业务唯一性,不要为了绕过报错直接削索引长度。
要点速览
utf8mb4一个字符最多按 4 个字节计算,VARCHAR(255)的索引预算可能达到 1020 字节。- InnoDB 常见 3072 字节上限不是“字符数上限”,联合索引还要把各列和长度前缀一起算进去。
- 前缀索引适合缩小扫描范围,但不能单独保证整列字符串唯一。
- 需要严格唯一时,优先缩短业务字段或增加确定性哈希辅助列,并保留碰撞后的完整值复核。
一次 utf8mb4 迁移,为什么会卡在索引创建
用用户邮箱做登录标识是常见场景:
CREATE TABLE account ( id BIGINT PRIMARY KEY, email VARCHAR(255) NOT NULL, display_name VARCHAR(120) NOT NULL, UNIQUE KEY uk_account_email (email) ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
计算时要看索引涉及的字节数,不要只看 255 这个字符数。单列邮箱通常还没触到 InnoDB 上限,但历史表常见的是多列联合唯一键,例如租户、邮箱、来源一起参与约束;再叠加较长的 VARCHAR,总预算很快就会超过限制。
SHOW FULL COLUMNS FROM account; SHOW CREATE TABLE account;
先确认真实字符集、排序规则、存储引擎和索引列顺序。某些环境的默认排序规则不同,比较规则和索引大小也可能与开发库不一致,不能只凭迁移脚本里的旧定义下结论。

先算清预算,再决定缩哪一部分
估算一条索引的粗略方法是把每个字符串列的最大字符数乘以字符集最大字节数,再加上其他列的固定字节与索引开销。它不是替代数据库验证的精确公式,却足以在设计阶段发现明显超预算的定义。
-- 例如租户编号 + 邮箱的联合唯一键 tenant_id BIGINT -- 8 字节 email VARCHAR(255) -- utf8mb4 按最大 4 字节估算:1020 字节
如果只是查询邮箱前缀,可以使用前缀索引:
ALTER TABLE account ADD KEY idx_email_prefix (email(191));
但这里要特别留意:email(191) 只比较前 191 个字符。它能帮助 LIKE 'abc%' 或相似条件缩小范围,却不能保证两个前缀相同、后半段不同的邮箱不会同时写入。把它直接改成唯一前缀索引,是一个很容易把数据约束悄悄改弱的决定。
三种方案的取舍:查询快、约束强,不能混为一谈
方案一:缩短业务字段
如果业务能明确邮箱、外部账号或订单号的最大长度,直接把列收紧到合理值最简单。迁移前先统计现有最大长度和超长样本,确认应用校验、接口文档、导入脚本都接受新边界。这种方案的约束最直观,索引也最容易理解。
方案二:普通前缀索引
它适合搜索和排序,不适合作为完整唯一性证明。前缀长度要根据数据分布测量,不是固定照搬 191;可以对不同长度做基数统计,观察前缀区分度是否足够。
SELECT COUNT(*) AS total_rows, COUNT(DISTINCT LEFT(email, 120)) AS distinct_prefix FROM account;
方案三:哈希辅助列加完整值复核
需要严格唯一、原字符串又不方便缩短时,可以增加固定长度的确定性摘要列并建立联合唯一索引:
ALTER TABLE account
ADD COLUMN email_sha BINARY(32)
GENERATED ALWAYS AS (UNHEX(SHA2(LOWER(email), 256))) STORED,
ADD UNIQUE KEY uk_email_sha (email_sha);
生产实现不要把哈希碰撞当成数学上绝对不可能。写入流程仍应在摘要命中后用完整 email 做二次判断;若结果不同,记录冲突并停止写入。若排序规则要求大小写或重音不敏感,摘要输入也必须与数据库比较语义一致。

迁移前后要验证四个结果
- 结构验证:
SHOW CREATE TABLE中字符集、排序规则、列长度和索引顺序符合设计。 - 数据验证:现有值没有被截断,大小写、重音和尾部空格的比较结果符合业务预期。
- 约束验证:故意写入相同完整值应失败;只共享前缀但完整值不同的样本应按设计处理。
- 性能验证:对真实分布运行
EXPLAIN,确认搜索条件使用了目标索引,而不是只看索引名称存在。
大表变更还要单独评估执行窗口、空间峰值和回退方式。可以先在副本上重放数据,再用低峰流量做小批量验证;不要为了躲过一次长度报错,直接在线上删掉旧约束。
相关问题:前缀索引和唯一索引怎么选
utf8mb4 一定要把 255 改成 191 吗?
不一定。191 是历史上常见的保守值,不是所有表都必须遵循的规则。应根据 MySQL 版本、索引类型、联合列总长度和业务字段上限计算并验证。
唯一前缀索引能防止邮箱重复吗?
只能防止索引前缀重复,不能证明完整邮箱重复。登录标识这类强约束字段,优先使用完整可比较的值、缩短字段或哈希辅助列方案。
排序规则会影响唯一索引吗?
会。大小写、重音以及某些尾部空格的比较规则会决定哪些值被认为相同。迁移前要用业务样本验证,而不是只看列定义中的字符集名称。
把索引长度问题当成数据模型问题处理
索引报错只是表结构把真实约束暴露出来的时刻。查询型前缀索引、严格唯一约束和字符串搜索是三件事,分别设计、分别验收,后续维护会比“统一改成 191”可靠得多。完成迁移后,把字符集、排序规则、字段上限与唯一性规则写进表结构说明,下一次扩展字段时就不会重新踩同一个坑。
-
374 收藏
-
499 收藏
-
384 收藏
-
184 收藏
-
265 收藏
-
300 收藏
-
109 收藏
-
421 收藏
-
419 收藏
-
238 收藏
-
数据库 · MySQL | 1天前 | MySQL · 回滚 · 数据库运维 · 配置变更 · 系统变量 · MySQL 8.4 SET PERSIST SET PERSIST_ONLY RESET PERSIST mysqld-auto.cnf persisted_variables296 收藏
-
数据库 · MySQL | 1天前 | MySQL · 回滚 · 数据库运维 · 配置变更 · 系统变量 · MySQL 8.4 SET PERSIST SET PERSIST_ONLY RESET PERSIST mysqld-auto.cnf persisted_variables244 收藏
-
数据库 · MySQL | 1天前 | MySQL · sql优化 · 数据库运维 · 性能排查 · 优化器提示 · MySQL 8.4 SET_VAR optimizer hint sort_buffer_size SQL 性能隔离497 收藏
-
数据库 · MySQL | 2天前 | MySQL · DDL · 元数据锁 · 性能排查 · performance_schema · MySQL 元数据锁 metadata_locks performance_schema DDL阻塞 Waiting for table metadata lock297 收藏
-
数据库 · MySQL | 2天前 | MySQL · 索引 · 执行计划 · sql优化 · 性能排查 · 执行计划 MySQL 8.4 EXPLAIN FORMAT=JSON cost_info query_cost239 收藏
-
数据库 · MySQL | 2天前 | MySQL · 索引 · 执行计划 · sql优化 · 性能排查 · 执行计划 MySQL 8.4 EXPLAIN FORMAT=JSON cost_info query_cost284 收藏
-
数据库 · MySQL | 3天前 | MySQL · 复制 · 主键 · InnoDB · 数据库迁移 · 数据库迁移 MySQL 8.4 sql_generate_invisible_primary_key 生成不可见主键 my_row_id419 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习