应通过awr报告中sql statistics→sql id details→view sql plan(需含+peeked_binds)查看绑定值,并结合dba_tab_col_statistics中histogram=none且density失真来定位数据倾斜字段。
直接看 awr 里 sql 的 card(cardinality)预估是否严重偏离实际,再核对绑定值和直方图状态——数据倾斜本身不会报错,但会让优化器选错计划,而 aw r 会忠实记录这种“误判”的后果。
为什么单看执行计划看不出数据倾斜?
执行计划里 INDEX RANGE SCAN 看起来很健康,NESTED LOOPS 也带了 BY INDEX ROWID BATCHED,但实际跑起来 Buffer Gets 翻 10 倍。问题不在操作符本身,而在优化器对 cardinality 的预估:它以为某值只返回 10 行,实际是 29 万行。AWR 不显示“预估 vs 实际”对比,但会暴露后果:
- 同一
sql_id在不同快照中plan_hash_value不变,但Buffer Gets Per Exec波动剧烈(比如从 1200 跳到 150000) - “SQL ordered by Gets”里该 SQL 排名突升,但“SQL ordered by Executions”里它执行频次没变
- “Top Segments by Logical Reads”指向的表,其对应字段在
dba_tab_col_statistics中histogram = 'NONE',且density明显失真(如NUM_DISTINCT = 100但某值占 95% 数据)
怎么从 AWR 报告里定位倾斜字段和绑定值?
别翻“Instance Efficiency”或“Load Profile”,重点盯两处:
- 进 “SQL Statistics” → 找目标 SQL → 点进 “SQL ID Details”,看 “Plan Hash Value” 行右侧的 “View SQL Plan” 链接;点击后跳转的计划页里,必须确认
format参数含+PEEKED_BINDS,否则看不到当时代入的绑定值(比如:B1 = '10'这种热点值) - 查该 SQL 对应的表字段直方图:运行
SELECT column_name, histogram, num_distinct, density FROM dba_tab_col_statistics WHERE owner = 'CJC' AND table_name = 'T1' AND column_name = 'OBJECT_ID';—— 若histogram = 'NONE'且density * num_distinct ≈ 1,基本可断定未收集直方图导致优化器误判 - 若用了绑定变量但没收集直方图,
PEEKED_BINDS显示的值就是第一次硬解析时窥探到的那个值,后续所有执行都复用这个基数预估,哪怕传的是极端偏态值
AWR 时间段选择要避开“平均化陷阱”
数据倾斜引发的性能波动常是脉冲式的:某条 SQL 因传入热点值卡住 3 秒,其余时间正常。如果按整点截取 AWR(如 14:00–15:00),这 3 秒可能被稀释成“平均等待 0.002 秒”,彻底隐身:
- 必须用
awrddrpt.sql做对比,基线选“无倾斜时段”(如夜间低峰),对比时段选“用户反馈卡顿的精确分钟级窗口”,比如 14:12–14:13 - 检查对比报告中 “SQL Statistics” 页的 “Execs per Sec” 和 “Elapsed Time per Exec” 列:若前者稳定、后者突增 >5 倍,且集中在某一条 SQL,就是倾斜信号
- RAC 环境下,确认
dba_hist_active_sess_history中该时段的session_state = 'WAITING'且event = 'db file sequential read'的会话,是否全部落在同一实例、同一数据块(p1text = 'file#' and p2text = 'block#')
真正难的不是发现倾斜,而是确认“这个值到底算不算倾斜”——业务上合理的长尾分布(如 VIP 用户订单量天然高),和统计上必须干预的病态倾斜(如 95% 订单挤在同一个分区键值),边界模糊。AWR 只给现象,判断得靠你手里的业务语义和采样数据。











