需先聚合慢日志并聚焦rows_examined远大于rows_sent的语句,再结合explain format=json、performance_schema及真实流量验证索引效果。

如何从 slow_log 找出真正需要优化的 SQL
慢查询日志本身不告诉你“该加什么索引”,只暴露“哪条语句慢”。关键在过滤和归因:先用 mysqldumpslow 或 pt-query-digest 聚合,重点关注 Rows_examined 远大于 Rows_sent 的语句——这通常意味着全表扫描或索引失效。
- 避免只看
Query_time排序:缓存命中、瞬时 IO 峰值可能干扰判断 - 检查
EXPLAIN时必须带上FORMAT=JSON,关注key是否为NULL、type是否是ALL或index - 注意
Using filesort和Using temporary:即使走了索引,排序/分组没走索引也会拖慢
ALTER TABLE ADD INDEX 的三个危险操作
线上加索引不是“加完就快”,MySQL 5.6+ 虽支持 ALGORITHM=INPLACE,但仍有隐性阻塞和空间风险。
- 对大表执行
ADD INDEX前,务必确认innodb_online_alter_log_max_size足够(默认 128MB),否则会触发临时文件写满失败 - 复合索引字段顺序不能拍脑袋:WHERE 条件中等值查询字段必须前置,范围查询(
>、BETWEEN)字段只能放在最后,否则后续字段无法命中 - 避免在
TEXT/VARCHAR(2000)列上直接建全文索引以外的索引——InnoDB 会截断前 767 字节(innodb_large_prefix=ON且ROW_FORMAT=DYNAMIC下可到 3072 字节)
为什么 EXPLAIN 显示 key != NULL,但 still 慢
索引被选中不等于高效利用。常见原因包括数据分布倾斜、统计信息过期、隐式类型转换。
- 执行
ANALYZE TABLE强制更新统计信息,尤其在大批量 INSERT/DELETE 后 - 检查 WHERE 条件是否触发隐式转换:比如
user_id是INT,但传入字符串'123',会导致索引失效 - 用
SELECT COUNT(*) FROM t WHERE indexed_col = ?验证选择性:如果返回行数占全表 20% 以上,优化器大概率放弃走索引
慢日志 + performance_schema 联动定位真实瓶颈
单靠 slow_log 只能看到“SQL 慢”,但不知道是锁等待、磁盘 IO 还是 CPU 密集。这时候要查 performance_schema.events_statements_history_long 和 events_waits_history_long。
- 开启必要消费者:
UPDATE setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE 'events_%_history%'; - 关联查出某慢 SQL 的等待事件:
SELECT EVENT_NAME, SOURCE, TIMER_WAIT FROM performance_schema.events_waits_history_long WHERE THREAD_ID IN (SELECT THREAD_ID FROM performance_schema.events_statements_history_long WHERE SQL_TEXT LIKE '%your_slow_sql%'); - 若发现大量
wait/io/file/innodb/innodb_data_file,说明是磁盘读取瓶颈;若为wait/synch/mutex/innodb/%,可能是并发更新冲突
EXPLAIN 输出,要在生产流量低峰期用 SELECT ... FOR UPDATE 或真实业务参数重放一次,观察 Rows_examined 是否下降一个数量级。











