JOIN用字符串字段慢因无索引导致全表扫描,前缀索引需满足等值匹配且前缀区分度高(如COUNT(DISTINCT LEFT(code,4))/COUNT(*)>0.95),否则失效;复合索引顺序、字符集一致性、在线加索引的资源争抢及数据增长后前缀长度失效均需关注。

为什么 JOIN 用字符串字段特别慢?
因为没索引的字符串字段在 JOIN 时只能走全表扫描,尤其是 VARCHAR(255) 这种长字段,MySQL 得逐行比对整个字符串——哪怕你只查前3位相同的数据。更糟的是,如果字段里还带空格、大小写混用或前后有不可见字符,ON t1.code = t2.code 实际会跳过大量本该匹配的行。
VARCHAR 字段加前缀索引后 JOIN 真的能用上吗?
能,但得满足两个硬条件:一是 JOIN 条件必须是等值匹配(=),不能是 LIKE '%xxx' 或函数包裹(比如 UPPER(code));二是前缀长度得覆盖绝大多数区分度。比如 code 字段前4位就决定了99%的唯一性,那 INDEX idx_code (code(4)) 就够用;但如果前6位还有大量重复,加 (4) 索引等于白建。
- 建索引前先跑
SELECT COUNT(DISTINCT LEFT(code, 4)) / COUNT(*) FROM table;,结果 > 0.95 才值得 - 避免用
(255)直接截全字段——MySQL 5.7+ 对前缀索引长度有限制(InnoDB 单列前缀最多767字节,utf8mb4 下约191字符) - 复合索引里含前缀字段时,顺序很重要:
(status, code(4))在WHERE status=1 AND code='ABCD123'场景下才生效
前缀索引导致 ORDER BY 或 GROUP BY 失效怎么办?
前缀索引只存前N个字符,MySQL 拿不到完整值,所以 ORDER BY code 会退化为文件排序(Using filesort),GROUP BY code 也可能分组错误。这不是 bug,是设计使然——索引不是数据副本。
- 如果业务真要按完整字符串排序/分组,别依赖前缀索引,改用生成列 + 索引:
ALTER TABLE t ADD code_full VARCHAR(255) STORED AS (code) VIRTUAL; CREATE INDEX idx_code_full ON t(code_full); - 或者把排序逻辑提到应用层,数据库只负责关联和过滤
-
EXPLAIN里看到key_len明显小于你设的前缀长度(比如设了(6)但显示key_len=3),说明用了字符集转换或隐式类型转换,得查COLLATION是否一致
线上表加前缀索引会不会锁表?
MySQL 5.6+ 的 ALGORITHM=INPLACE 支持在线加索引,但前提是存储引擎是 InnoDB 且没用到不支持的选项。不过“在线”不等于“无感”——大表加索引期间,DML(尤其是 INSERT/UPDATE)延迟会上升,因为索引构建要争抢 buffer pool 和 I/O 资源。
- 加索引前先看
SHOW PROCESSLIST有没有长事务,有就得等它结束,否则 DDL 会被堵住 - 用
pt-online-schema-change更稳妥,但要注意它会在原表上建触发器,如果JOIN字段本身更新频繁,触发器开销可能反超收益 - 测试环境务必用真实数据量压测:10万行快不代表1000万行也快,前缀索引的区分度会随数据增长劣化
真正麻烦的不是建索引,而是后续字段内容变化后没人去校验前缀长度是否还够用——今天 (4) 够,半年后新数据全变成 ABC-XXXX 格式,前4位又撞车了。











