SQL 性能监控与调优

1次阅读

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

SQL 性能监控与调优

查慢查询别只看 EXPLAIN,先抓真实执行时间

很多同学一遇到慢 sql 就立刻 EXPLAIN,但实际拖慢系统的往往不是执行计划本身,而是锁等待、磁盘 I/O 或临时表膨胀。EXPLAIN 只告诉你“理论上怎么走”,不反映“实际上卡在哪”。

实操建议:

  • 优先查 information_schema.PROCESSLISTmysql)或 pg_stat_activitypostgresql),过滤 state = 'active'backend_startstate_change 时间差大的会话
  • SHOW PROFILE FOR QUERY N(MySQL)或 EXPLAIN (ANALYZE, BUFFERS)(PostgreSQL)拿到真实耗时分布,重点关注 Execution TimeI/O Read Time
  • 避免在业务高峰期跑 EXPLAIN ANALYZE,它会真实执行语句,可能加重负载

索引失效的典型场景:隐式类型转换和函数包裹

加了索引却没走,八成是字段被隐式转换或套了函数。数据库无法用索引做等值/范围查找,只能全表扫描。

常见错误现象:

  • WHERE user_id = '123'user_idint字符串强制转整数,索引失效)
  • WHERE date(created_at) = '2024-01-01'(对字段用函数,索引失效)
  • WHERE status IN ('1', '2', '3')statusTINYINT,字符串列表触发隐式转换)

实操建议:

  • 统一参数类型:应用层传 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 字段)
  • 用游标分页替代 OFFSETWHERE 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 查当前锁等待链,重点看 TRANSACTIONSLOCK WAIT 部分
  • 定期导出 performance_schema.table_io_waits_summary_by_table,识别高频物理读表
  • 不要依赖默认监控粒度:把采样间隔从 60s 缩到 5s,才能捕获短时尖峰

最常被忽略的是“查询重写后没验证执行计划是否真变了”——加了索引、改了写法,一定要用 EXPLAIN format=json 对比 keyrows_examinedused_columns 这几项,而不是只看有没有 Using index

text=ZqhQzanResources