先看慢查询日志定位真实瓶颈,再用EXPLAIN分析执行计划;需开启慢日志、设long_query_time=1、关注频次×耗时、区分锁等待与真慢;EXPLAIN重点看type、key、Extra三列;索引要“刚好够用”,避免冗余、低基数、函数失效等问题;优化不能替代架构设计缺陷。

先看慢查询,再查执行计划——这是 MySQL 索引优化唯一靠谱的起点。 没有慢 SQL 日志和 EXPLAIN 输出,所有“加索引”“改字段”的操作都是蒙眼贴膏药。
从慢查询日志定位真实瓶颈
线上库不开启慢查询日志,等于开车不看油表。默认它关着,必须手动打开:
SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1; -- 超过1秒记为慢SQL SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
关键点:
-
long_query_time建议设为 1(秒),太低会刷屏,太高漏掉隐患; - 日志路径需确保 MySQL 进程有写权限,否则静默失败;
- 别只盯单条 SQL 的耗时——要关注 频次 × 平均耗时,高频中等耗时的语句往往比低频超长语句更伤性能;
- 注意区分是“锁等待”还是“真慢”,
Rows_examined和Lock_time在日志里都带出来,别只看Query_time。
用 EXPLAIN 看懂索引到底用没用上
对慢 SQL 执行 EXPLAIN,重点盯三列:type、key、Extra:
-
type = ALL:全表扫描,紧急加索引或重构查询; -
key = NULL:明明建了索引却没走,大概率是WHERE条件用了函数、隐式类型转换或OR拆分失效; -
Extra出现Using filesort或Using temporary:排序/分组没走索引,得补ORDER BY字段到复合索引末尾; -
key_len值比预期小:说明只用了复合索引前缀,检查WHERE是否满足最左前缀法则。
示例:
EXPLAIN SELECT name, age FROM user_info WHERE name = 'xys' AND age > 20 ORDER BY age;
理想情况:key = name_index(假设你建的是 (name, age) 复合索引),Extra 无 filesort;如果 key = NULL,就该查查 name 字段是不是被 UPPER(name) 包裹了。
建索引不是越多越好,而是“刚好够用”
索引是双刃剑:读快了,写就慢;空间占了,缓存就挤。建之前问自己三个问题:
- 这个字段在
WHERE、JOIN、ORDER BY或GROUP BY里高频出现吗?没出现就别建; - 它的基数(distinct 值数量)够高吗?比如
status只有 '0'/'1',建索引几乎无效; - 是否能合并?比如已有
(a, b)索引,就别单独再建a单列索引——它已被覆盖。
常见误操作:
- 给
VARCHAR(255)字段直接建全文索引——除非真做模糊搜索,否则优先考虑前缀索引:INDEX idx_name (name(10)); - 在
DATETIME字段上建单列索引却总查DATE(create_time)——函数导致索引失效,应改用范围查询:create_time >= '2025-12-01' AND create_time ; - 对
TEXT或大JSON字段建普通索引——MySQL 不支持,得用生成列 + 索引或全文索引。
索引维护比创建更重要
索引不是一劳永逸。InnoDB 的 B+Tree 在大量增删后会碎片化,SELECT 效率下降,但 SHOW INDEX 看不出异常:
- 定期检查索引碎片率:
SELECT table_name, data_free / data_length AS frag_ratio FROM information_schema.tables WHERE table_schema = 'your_db';,> 0.3 就该考虑重建; - 重建索引别用
DROP + CREATE——锁表。用ALTER TABLE t ENGINE=InnoDB;(在线 DDL,MySQL 5.6+ 支持); - 删除无用索引前,先用
sys.schema_unused_indexes视图(需启用 performance_schema)确认它真没人用;盲目删可能让某条夜间报表 SQL 突然变慢十倍。
最后提醒一句:索引优化解决不了设计缺陷。如果一张表连主键都没有、或者 JOIN 总是跨 5 张大表,加再多索引也只是延缓崩溃——该拆表拆表,该加冗余加冗余,别拿索引当创可贴。










