查询执行计划退化(突然变慢)
昨天还很快的查询今天突然变慢。通常是优化器切换到了更差的执行计划。我们从过时的统计信息、参数敏感性、数据量增长或执行计划突变中找出根本原因,并稳定执行计划。
执行计划真的变了吗?何时变的?
同一查询是否存在多个执行计划(plan_hash/plan_generation),变慢是从何时开始的?是否与最近的部署、升级、参数变更或大规模数据加载同时发生?
SELECT sql_id, plan_hash_value, executions, ROUND(elapsed_time/GREATEST(executions,1)/1e6,3) AS sec_per_exec, first_load_time
FROM v$sql WHERE sql_id='&sqlid' ORDER BY first_load_time;- 统计信息可能已过时
- 同一查询因参数不同而表现不同(sniffing/peeking)
- 数据量显著增长
- 升级/flush 之后执行计划突然改变
统计信息是最新的吗?
优化器依据统计信息做出决策。如果最近一次 analyze 的日期较早或修改计数较高,估算可能确实已经偏离,从而导致糟糕的执行计划。
SELECT table_name, num_rows, last_analyzed, stale_stats
FROM dba_tab_statistics WHERE table_name='T';- 统计信息过时/缺失
- 统计信息是最新的,但执行计划仍对参数敏感
是否存在参数敏感性?
查询根据首次执行时的参数值选择并缓存执行计划(MSSQL parameter sniffing / Oracle bind peeking)。对于分布倾斜的其他值,该执行计划可能很糟糕。
SELECT sql_id, child_number, plan_hash_value, is_bind_sensitive, is_bind_aware
FROM v$sql WHERE sql_id='&sqlid';- 是的,执行计划随参数在好坏之间切换
- 已知正确的执行计划,我想永久固定它
根本原因:统计信息过时 / 缺失
基于与实际不符的旧统计信息,优化器错误估算了行数,选择了糟糕的执行计划。这是执行计划退化最常见的原因。
根本原因:参数敏感性(sniffing / bind peeking)
缓存的执行计划是针对首个到达的参数值选择的,对于分布倾斜的其他值来说很糟糕。目标是确保选择与参数值相适应的执行计划。
解决方案:固定正确的执行计划(baseline / force plan)
如果存在已知良好的执行计划,将其固定以使优化器不再偏离,可以永久阻止退化。
根本原因:数据量增长,执行计划不再适用
随着表的增长,过去理想的执行计划(例如 nested loop)如今扩展性很差;优化器需要能够正确反映增长的统计信息和访问路径。
根本原因:执行计划突变(升级 / 缓存清空)
在版本升级、参数变更或执行计划缓存被清空之后,执行计划突然改变。优化器行为或环境条件发生了变化。