慢查询:深入诊断
某个查询或整个系统很慢。逐步缩小范围:在每个节点执行检查,并根据结果选择分支。最终得到根本原因 + 修复方案。
什么慢?
是一个或少数几个特定查询慢,还是整个系统慢?据此将排查分为两条路径之一:“以查询为中心”或“以资源/瓶颈为中心”。
- 一个或少数几个特定查询慢
- 整个系统都慢
查询此刻正在运行吗?
是此刻正在运行/卡住的查询,还是“一直都慢”的查询?查看活动会话。
SELECT sid, serial#, sql_id, last_call_et AS running_sec, event, blocking_session
FROM v$session WHERE status='ACTIVE' AND type='USER' ORDER BY last_call_et DESC;- 此刻正在运行/卡住
- 一直都慢;我想看它的执行计划
查询是否在等待什么?
如果它处于活动状态却没有进展,很可能是在等待。blocking_session / wait_event 是否有值?
SELECT sid, blocking_session, event, seconds_in_wait
FROM v$session WHERE sid=&target_sid;- 被另一个会话阻塞
- 在等待资源(IO / 日志 / latch)
- 没有等待;正在大量消耗 CPU
哪种等待占主导?
占主导的等待事件说明了瓶颈的类型。
SELECT event, total_waits, time_waited FROM v$session_event WHERE sid=&target_sid ORDER BY time_waited DESC FETCH FIRST 5 ROWS ONLY;- IO 等待(db file read / PAGEIOLATCH)
- 日志/提交等待(log file sync / WRITELOG)
- 锁/队列(enq: TX / LCK)
获取执行计划(EXPLAIN)
取出执行计划。是否有全表扫描、意外的连接顺序,或估算行数与实际行数之间的差距?
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', NULL, 'ALLSTATS LAST'));- 全表扫描/缺少索引
- 昨天还很快;执行计划突然变了
- 处理的行太多 / N+1
- 执行计划看起来不错但仍然慢
统计信息是否最新?
估算行数与实际行数(E-rows 与 A-rows)差距很大,是统计信息过时的迹象。
SELECT table_name, num_rows, last_analyzed FROM dba_tables WHERE table_name='&TABLE_NAME';- 统计信息过时/陈旧
- 统计信息是最新的但仍然慢
系统级:瓶颈在哪里?
如果是所有操作都慢而不是单个查询,就要查找资源瓶颈:CPU、IO、内存,还是大范围阻塞?
SELECT metric_name, value FROM v$sysmetric WHERE metric_name IN ('Host CPU Utilization (%)','Average Active Sessions') AND intsize_csec=(SELECT MAX(intsize_csec) FROM v$sysmetric);- CPU 接近 100%
- IO 等待高
- 内存压力/缓存命中率低
- 大量阻塞
根本原因:阻塞
查询在等待另一个会话持有的锁。找到链条最前端的会话(根阻塞者)并加以处理。
根本原因:IO 瓶颈
查询从磁盘读取大量数据。通常源于全表扫描(缺少索引)或内存(buffer cache)不足。
根本原因:提交/日志瓶颈
若 'log file sync' / 'WRITELOG' 占主导,说明提交在等待 redo/WAL 写入。
根本原因:缺少索引
执行计划在做全表扫描。在过滤/连接列上建立合适的索引会大幅提速。
根本原因:执行计划回归
查询昨天还快,今天却不快了;执行计划变了。通常是统计信息过时或参数变更所致。
根本原因:查询设计
执行计划合理,但查询本身做了太多工作(行太多、N+1、不必要的连接)。
根本原因:统计信息过时
由于统计信息不是最新的,优化器估算错误。
深入分析
若执行计划和统计信息都没问题但仍然慢,就要深入挖掘。
根本原因:CPU 瓶颈
整个系统受 CPU 限制。通常是少数几个执行计划糟糕的查询在大量消耗 CPU。
根本原因:内存压力
缓存命中率低和/或发生 swap:内存不足或分配不当。