说几个关键要点。若要从ASH(活动会话历史)中精准定位热点数据块,必须依赖current_file#和current_block#这两个字段,它们是唯一能精确锁定具体数据块的标识。不要去查询sql_text或event的模糊匹配,那样做基本是徒劳。

过滤条件必须设置为event IN ('buffer busy waits', 'read by other session'),并且sample_time > SYSDATE - 1/24,即只取最近1小时的数据,这样可以有效避免历史噪声的干扰。另外,current_obj# = 0不代表没有对应对象,它可能只是刚进入逻辑读阶段、尚未绑定段名,此时更要重点关注current_file#和current_block#。执行聚合查询时,推荐使用FETCH FIRST 5 ROWS ONLY快速获取Top热点,避免全表扫描导致效率低下。
SELECT current_file#, current_block#, COUNT(*) cnt
FROM v$active_session_history
WHERE event IN ('buffer busy waits', 'read by other session')
AND sample_time > SYSDATE - 1/24
GROUP BY current_file#, current_block#
ORDER BY cnt DESC
FETCH FIRST 5 ROWS ONLY;
如何将file#/block#映射到真实段和对象?
直接使用DBA_OBJECTS.object_id进行关联会出错——current_block#对应的是逻辑块号,而DBA_OBJECTS.object_id并不等于X$BH.obj,必须通过DBA_EXTENTS做区间匹配才能正确映射。
查询DBA_EXTENTS时,条件必须写成 &BLOCK_ID BETWEEN block_id AND block_id + blocks - 1,而不能使用简单的= block_id。一个块只属于一个extent,但可能跨多个segment(例如分区表)。如果查不到结果,说明该块属于系统段:回滚段、临时段或UNDO块,此时应转向v$rollstat或v$tempseg_usage。对于LOB段,需要先查询DBA_LOBS找到真实段名(例如SYS_LOB0000098765X$$),再针对该段进行操作,单独修改LOB列存储参数是无效的。
为什么不能跳过P3值?
P3在Oracle 12c中代表class#,即块类型编号,而非原因码。如果跳过这一步就直接调整参数,基本是白费功夫。不同类型的块,优化方向截然不同:
P3 = 1:普通数据块争用,常见于升序主键INSERT或小表全表扫描P3 = 4:段头争用,说明仍在使用MSSM表空间,FREELIST成为单点瓶颈P3 = 17或18:UNDO段头或UNDO块紧张,此时应扩展UNDO表空间,而不是调整UNDO_RETENTIONP3 = 130:块正被其他会话从磁盘读入Buffer Cache,本质上是read by other session的前兆
RAC环境下排查热点块有哪些常见误区?
在RAC中,前台进程不会直接申请锁,而是由LCK0进程代为协调,等待超时默认60秒(单实例为3秒)。如果观察到大量row cache lock且p1指向dc_sequences,90%的原因是序列未设置CACHE——每次NEXTVAL都会触发全局协调。
不要使用v$session_wait来排查RAC热点,该视图反映的是本地等待信息。应优先使用v$active_session_history,并注意inst_id字段。另外,预分配extent在RAC下仍需跨实例协调,单节点操作无法消除全局HW锁争用。如果buffer busy waits和gc buffer busy acquire同时出现,必须先分离等待类型——否则增加freelist、扩展undo都是徒劳。
实际操作中,最容易被忽略的是current_file#/current_block#与DBA_EXTENTS的区间匹配逻辑,以及RAC下LCK0带来的等待延迟特征。这两点一旦判断错误,后续所有优化动作都会偏离真正的根因。
