查慢查询应先抓真实执行时间而非只看explain,因锁等待、i/o或临时表膨胀常是主因;再检查活跃会话与真实耗时分布,避免高峰期执行分析命令,并警惕隐式转换和函数包裹导致索引失效。

查慢查询别只看 EXPLAIN,先抓真实执行时间
很多同学一遇到慢 sql 就立刻 EXPLAIN,但实际拖慢系统的往往不是执行计划本身,而是锁等待、磁盘 I/O 或临时表膨胀。EXPLAIN 只告诉你“理论上怎么走”,不反映“实际上卡在哪”。
实操建议:
- 优先查
information_schema.PROCESSLIST(mysql)或pg_stat_activity(postgresql),过滤state = 'active'且backend_start与state_change时间差大的会话 - 用
SHOW PROFILE FOR QUERY N(MySQL)或EXPLAIN (ANALYZE, BUFFERS)(PostgreSQL)拿到真实耗时分布,重点关注Execution Time和I/O Read Time - 避免在业务高峰期跑
EXPLAIN ANALYZE,它会真实执行语句,可能加重负载
索引失效的典型场景:隐式类型转换和函数包裹
加了索引却没走,八成是字段被隐式转换或套了函数。数据库无法用索引做等值/范围查找,只能全表扫描。
常见错误现象:
-
WHERE user_id = '123'(user_id是int,字符串强制转整数,索引失效) -
WHERE date(created_at) = '2024-01-01'(对字段用函数,索引失效) -
WHERE status IN ('1', '2', '3')(status是TINYINT,字符串列表触发隐式转换)
实操建议:
- 统一参数类型:应用层传
123而非'123'给INT字段 - 改写函数逻辑:把
DATE(created_at) = '2024-01-01'换成created_at >= '2024-01-01' AND created_at - 用
SHOW WARNINGS(MySQL)看优化器是否提示Cannot use range access on index ... due to type or collation conversion
ORDER BY + LIMIT 的性能陷阱:避免回表排序
当 ORDER BY 字段不在索引覆盖范围内,又带 LIMIT,MySQL 可能先排序再截断,而不是边找边排——尤其在数据量大、排序字段无索引时,性能断崖式下跌。
使用场景:
- 分页查询:
select * FROM orders ORDER BY created_at DESC LIMIT 20 OFFSET 10000 - 榜单类需求:
SELECT name, score FROM users ORDER BY score DESC LIMIT 10
实操建议:
- 确保
ORDER BY字段有索引,且该索引能覆盖WHERE条件(复合索引顺序很重要:先WHERE字段,再ORDER BY字段) - 用游标分页替代
OFFSET:WHERE created_at - 检查
Extra列是否含using filesort(MySQL)或Sort Method: external merge(PostgreSQL)
监控指标别只盯 QPS 和慢日志,重点关注 buffer hit ratio 和 lock wait time
QPS 高不等于健康,慢日志漏掉大量“中等慢”查询;真正压垮数据库的,往往是缓存命中率骤降或长事务锁表。
关键指标含义与阈值参考:
-
innodb_buffer_pool_hit_ratio(MySQL):低于 95% 就要警惕,说明大量读落到磁盘 -
shared_buffers_hit_ratio(PostgreSQL):低于 98% 值得排查 -
Innodb_row_lock_time_avg(MySQL):超过 50ms 表示锁竞争严重 -
pg_stat_database.blks_read / pg_stat_database.blks_hit(PostgreSQL):比值突增说明缓存失效或查询未走索引
实操建议:
- 用
SHOW ENGINE INNODB STATUSG查当前锁等待链,重点看TRANSACTIONS和LOCK WAIT部分 - 定期导出
performance_schema.table_io_waits_summary_by_table,识别高频物理读表 - 不要依赖默认监控粒度:把采样间隔从 60s 缩到 5s,才能捕获短时尖峰
最常被忽略的是“查询重写后没验证执行计划是否真变了”——加了索引、改了写法,一定要用 EXPLAIN format=json 对比 key、rows_examined、used_columns 这几项,而不是只看有没有 Using index。