绑定变量窥探(Bind Peeking)指Oracle在SQL首次硬解析时窥视绑定变量实际值以生成执行计划,后续软解析复用该计划;若首次值极端(如返回500万行却估算100行),会导致全表扫描等低效计划被错误复用,引发瞬时性能抖动。

绑定变量窥探(bind peeking)在Oracle 19c中仍会引发执行计划“看似合理、实则失准”的问题,尤其当首次硬解析时传入的绑定值具有极端数据分布(比如 :status = 'Z' 返回500万行,而优化器按平均值估算仅选100行),后续软解析复用该计划就会严重变慢。ASH本身不存绑定值,但能精准锚定“哪次执行因窥探失准而卡住”,这是定位问题的关键起点。
查 v$active_session_history 锁定异常执行窗口
绑定变量窥探导致的性能抖动通常是瞬时、离散的——某次执行突然耗时飙升,其余时间正常。不能靠 dba_hist_sqlstat 这类聚合视图,必须用内存级采样:
- 执行时间必须精确到秒:
sample_time > SYSDATE - 2/1440(最近2分钟),太宽会混入正常样本 - 过滤条件要带
sql_id和event组合:WHERE sql_id = 'abc123xyz' AND event IN ('db file sequential read', 'direct path read', 'ON CPU') AND session_state = 'WAITING',避免把CPU密集型正常执行误判为问题 - 重点看
p1/p2:对db file sequential read,p1是文件号,p2是块号,若连续多行显示相同p1/p2,说明在反复读同一小表(可能是未走索引的全表扫描)
用 dbms_xplan.display_cursor 对比真实执行路径
ASH只告诉你“卡在哪”,不告诉你“为什么卡”。拿到可疑 sql_id 后,立刻查当前游标计划,重点验证窥探是否生效:
- 执行
SELECT * FROM TABLE(dbms_xplan.display_cursor('abc123xyz', NULL, 'outline +peeked_binds')) - 确认输出中是否含
Peeked Binds小节;若为空,说明本次执行未触发窥探(比如是软软解析且15分钟内刚捕获过) - 对比
Rows (Estim)和实际返回行数:若估算100行、实际扫500万行,且计划里出现TABLE ACCESS FULL,基本锁定是窥探失准 - 注意
Partition Start/Stop字段:若为KEY或ROW LOCATION,说明分区裁剪未生效,常因绑定值类型不匹配(如字符型字段传数字)导致
查 v$sql_bind_capture 验证绑定值与统计信息时效性
窥探依赖两个前提:绑定值被成功捕获,且对应表的统计信息足够新。二者缺一不可:
-
v$sql_bind_capture中查不到值?先确认是否满足捕获条件:in_parse = 'Y'且in_hard_parse = 'Y'的采样行是否存在(用v$active_session_history查);INSERT 的 VALUES 子句中的绑定值永远不被捕获 - 查
dba_tab_statistics看关键表的last_analyzed:若比问题发生时间早7天以上,且该表近期有大量DML,统计信息大概率已失效 - 特别注意
num_distinct和density:若绑定列的density异常低(比如 0.00001),而实际值高度倾斜,优化器会严重低估选择率
为什么不能只看 AWR 报告里的 SQL Statistics
AWR 的 SQL ordered by Elapsed Time 会把多次执行的耗时平均化。一次窥探失准的执行(耗时8秒)混在99次正常执行(各耗时20ms)里,平均值才100ms,根本进不了 Top SQL。更危险的是,它可能把问题 SQL 归类为 “Elapsed Time per Exec 正常”,直接漏掉。
真正有效的做法是:用 ASH 定位单次异常执行 → 用 display_cursor 看该次的真实计划和窥探值 → 用 v$sql_bind_capture 和 dba_tab_statistics 验证数据基础是否可靠。整个链条里,最容易被跳过的其实是“确认这次慢执行是否真触发了窥探”,而不是默认它发生了。











