当 MySQL 表中的字符串列(如 VARCHAR、TEXT)长度较大且查询主要匹配前缀时,应该使用前缀索引。如果完整列索引超过存储引擎限制(如 InnoDB 默认 767 字节)或占用过多空间,需指定前缀长度。风险边界是前缀索引无法用于 ORDER BY、GROUP BY 和覆盖索引扫描。
先说结论:前缀索引适合长字符串字段且查询条件为前缀匹配的场景,长度选择需平衡区分度与存储限制。
- 适合:VARCHAR 或 TEXT 类型列,查询使用 LIKE 'prefix%' 或等值匹配前缀。
- 重点看:索引选择性(区分度),需通过 SQL 计算不同长度下的 DISTINCT 比例。
- 别忽略:前缀索引不支持排序、分组和覆盖索引,联合索引中长字段需放在最后。
命令速用版
创建前缀索引直接在列名后指定字符长度,建表或修改表结构均可执行。
-- 建表时定义
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(255),
INDEX idx_email (email(10))
);
-- 已有表添加索引
CREATE INDEX idx_email_prefix ON users (email(10));
-- 联合索引(长字段放最后)
CREATE INDEX idx_status_email ON users (status, email(191));为什么会这样
使用前缀索引的核心原因是突破索引长度限制并节省存储空间。InnoDB 引擎默认单索引最大长度为 767 字节,utf8mb4 字符集下约等于 191 个字符,超过该长度必须指定前缀。
完整列索引对于长字符串会占用大量磁盘空间和内存,导致索引树过高,增加 I/O 消耗。前缀索引仅索引列值的前 N 个字符,能减小索引体积,提高插入速度和查询效率。但代价是索引选择性可能降低,且无法利用索引完成排序操作。
分步处理
第一步是评估字段区分度,确保前缀长度足够唯一标识数据。
执行 SQL 计算不同前缀长度的区分度,公式为 distinct_count / total_count,结果越接近 1 越好。
SELECT COUNT(DISTINCT LEFT(email, 10)) / COUNT(*) FROM users;
SELECT COUNT(DISTINCT LEFT(email, 12)), COUNT(DISTINCT email) FROM users;第二步是创建索引,注意字符集影响。非二进制字符串(VARCHAR)前缀按字符数计算,二进制字符串(BLOB)按字节数计算。
第三步是检查索引定义,确保长度参数已生效。如果字段类型是 TEXT 而忘记加长度,MySQL 可能自动忽略索引定义。
怎么验证是否生效
使用 SHOW INDEX 命令查看 Sub_part 列,确认前缀长度是否符合预期。
SHOW INDEX FROM users WHERE Key_name = 'idx_email';Sub_part 列显示数字(如 10 或 191)表示前缀长度生效,显示 NULL 表示全列索引。如果建了 email(191) 却看到 Sub_part 是 NULL,说明建索引语句没生效。
使用 EXPLAIN 分析查询语句,确认 type 列为 ref 或 range,且 key 列显示创建的索引名。如果查询条件未用上最左前缀,可能仍走全表扫描。
常见坑
联合索引中长字段带前缀必须置于最后,否则可能报错或查询效率受损。例如 (status, email(191)) 合法,但 (email(191), status) 在某些版本受限。
前缀索引不能作为唯一索引,因为前缀相同的数据会被视为重复。如果业务要求唯一性,必须使用完整列索引。
长度单位易混淆,utf8mb4 下 1 个中文等于 1 个字符而非 3 个字节,定义长度时按字符数指定,但受限于总字节数上限。
常见问题
前缀索引能用于 ORDER BY 吗?
不能。MySQL 无法利用前缀索引完成排序和分组操作,这类查询会触发文件排序。
如何确定最佳前缀长度?
通过逐步增加长度测试区分度,当区分度达到 90% 以上且不再显著增长时,即为合适长度。
所有字符串字段都适合前缀索引吗?
不适合。如果字段本身很短或前缀重复率极高(如大量邮箱同一域名),前缀索引优势不明显。
参考来源
- mysql 中前缀索引的使用方法与限制
- 技术分享 | MySQL 索引长度限制的案例
- 如何在 MySQL 中为特定列增加前缀索引_在 INDEX 定义中指定长度