<p>Buffer Busy Waits 的根因是物理块争用,需先通过 ASH 的 current_file# 和 current_block# 定位热点块,再结合 P3 值判断类型(如 220 为数据块行争用、4 为段头争用),最后查 dba_extents 确认段名及对象类型。</p>
buffer busy waits 不是“要优化 sql”,而是“有块被卡住了”——得先定位到具体是哪个文件、哪个块、哪类操作在争,否则调索引、加 buffer cache 都是白忙。
用 ASH 查 current_file# 和 current_block# 定位热点块
ASH 里没有直接的 “hot block ID”,但 current_file# 和 current_block# 是唯一能反推物理块位置的线索。关键不是查 SQL 文本(sql_text like '%UPDATE%' 永远不生效),而是看等待上下文:
- 过滤条件必须是
event = 'buffer busy waits'或更精准的event IN ('buffer busy waits', 'enq: TX - row lock'),时间范围建议 ≤1 小时,避免噪声淹没信号 -
current_obj#为 0 不代表没对象——可能刚进入逻辑读阶段,还没绑定具体段;此时更要盯紧current_file#和current_block# - 执行以下语句可快速聚合出 Top 热点块:
SELECT current_file#, current_block#, COUNT(*) cnt FROM dba_hist_active_sess_history WHERE event = 'buffer busy waits' AND sample_time > SYSDATE - 1/24 GROUP BY current_file#, current_block# ORDER BY cnt DESC FETCH FIRST 5 ROWS ONLY;
结果中的 current_file# 和 current_block# 可直接用于下一步查段名。
根据 file_id + block_id 反查段名和对象类型
拿到块号后,不能靠猜,得用 dba_extents 精确匹配所属段。注意:一个块只属于一个 extent,但可能跨多个 segment(比如分区表),所以查询必须带 partition_name 判断:
- 执行时把上一步的
<P1>替换为current_file#,<P2>替换为current_block# - 若查不到结果,说明该块是临时段、回滚段或 UNDO 块——这时要转向
dba_rollback_segs或v$rollstat - 常见误判:看到
segment_name是索引,就去重建索引;其实热块可能是索引叶块(class# = 220),也可能是索引段头(class# = 4)——二者处理方式完全不同
SELECT s.segment_name, s.partition_name, s.segment_type FROM dba_extents s WHERE <P2> BETWEEN s.block_id AND (s.block_id + s.blocks - 1) AND s.file_id = <P1>;
看 P3 原因码决定下一步动作
P3 是诊断分水岭,不同值对应完全不同的根因路径。别跳过这步直接改参数:
-
P3 = 130:块不在 buffer cache,多会话抢着从磁盘读——本质是 I/O 延迟暴露为 buffer wait,优先查db file sequential read是否同步飙升 -
P3 = 220:数据块内多行并发修改(如小表高频 UPDATE)——检查该块是否行密度高(8k 块存 100+ 行),考虑拆分或加ASSM表空间 -
P3 = 4:段头争用(常见于 MSSM 下高并发 INSERT)——确认是否用了FREELIST,改用ASSM或增加FREELISTS数量 -
P3 = 17或18:UNDO 相关——不是数据块问题,是回滚段头或 UNDO 块被占,需查undo_retention和事务平均时长
查 P3 必须结合 v$active_session_history 的原始记录,不要依赖 AWR 报告里的汇总值——它可能已丢失精度。
ASSM 下仍出现 buffer busy waits?检查是不是 reverse key index 搞的鬼
Reverse key index 能缓解叶块争用,但代价是范围扫描失效、执行计划突变。如果发现热点块集中在某个索引的叶块(segment_type = 'INDEX'),且 P3 = 220,别急着加 buffer pool:
- 先用
DBMS_ROWID.ROWID_CREATE构造伪 ROWID,查出该块里实际有哪些键值分布 - 确认是否是单边增长主键(如
SEQ.NEXTVAL)导致所有新值都挤在同一个叶块 - reverse key index 后,
INDEX RANGE SCAN会退化成FULL INDEX SCAN,代价可能更高——得权衡 QPS 提升 vs 单条响应恶化
真正难啃的 case 往往不是参数或 SQL,而是块级物理布局与业务写入模式的硬冲突。定位到 file_id 和 block_id 后,下一步永远是“这个块里到底存了什么”,而不是“怎么让 Oracle 更快地等”。


















