如何从 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是否下降一个数量级。
