先看慢查询,再查执行计划——这是 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 张大表,加再多索引也只是延缓崩溃——该拆表拆表,该加冗余加冗余,别拿索引当创可贴。
