应直接查v$active_session_history定位CBC latch争用源头,按p1聚合分析是否真热块,并用dba_extents反查p1对应对象,区分真热块、低效执行计划或row cache误判。
直接查 v$active_session_history 定位 CBC latch 争用源头
latch 争用不能只看等待总次数,得确认是不是真有 block 被反复争抢。最有效的方式是实时抓取 v$active_session_history 中的 latch: cache buffers chains 事件,按 p1(即 buffer#)聚合——因为每个 p1 值唯一对应一个内存 buffer header。
常见错误是把所有 CBC 等待全当成“热块”,其实可能是多个不同 block 映射到同一个 hash bucket(受 _db_block_hash_buckets 控制),造成 latch 拆分粒度不够的假象。
- 执行:
SELECT p1, COUNT(*) FROM v$active_session_history WHERE event = 'latch: cache buffers chains' GROUP BY p1 ORDER BY 2 DESC FETCH FIRST 5 ROWS ONLY - 如果返回的
p1值集中在一个极小范围(比如连续几个 block 号),再结合业务逻辑判断是否为索引根块、主键叶块等固定访问路径 - 若
p1分散但spin_gets / gets比例 > 30%,说明 latch 自旋失败多,是全局性争用,优先检查_db_block_hash_buckets是否过小
用 dba_extents 反查 p1 对应的真实对象
p1 是 data block address(DBA)的十进制表示,需先拆成 file_id 和 block_id 才能定位对象。别跳过这步——很多“热块”其实是系统表或字典缓存块,不是业务表。
Oracle 提供了 dbms_utility.data_block_address_file 和 dbms_utility.data_block_address_block 函数来解码,但更稳的方式是用 dba_extents 直接查:
- 先算出 file_id:
SELECT dbms_utility.data_block_address_file(&p1) FROM dual - 再算出 block_id:
SELECT dbms_utility.data_block_address_block(&p1) FROM dual - 最后查对象:
SELECT owner, segment_name, segment_type FROM dba_extents WHERE file_id = :file AND :block BETWEEN block_id AND block_id + blocks - 1
注意:如果反查结果是 SYSTEM 或 SYS 下的小对象(如 obj$、tab$),大概率是 row cache objects latch 争用被误判为 CBC;这时该去查 enq: row cache objects 事件本身。
区分 CBC 争用是真热块还是低效执行计划导致
加索引后 CBC 更忙,往往不是因为数据热,而是执行计划变差:比如对低基数字段建了非选择性索引,导致大量 INDEX RANGE SCAN + TABLE ACCESS BY INDEX ROWID,反复回表访问同一堆表头几个 block。
这种场景下,热点不在索引块本身,而在堆表的物理存储位置——尤其当表很小、所有行挤在前几个 data block 时,cache buffers chains latch 就会集中在那几个 buffer header 上。
- 检查执行计划中
ROWS预估是否严重高估,ACCESS PREDICATES是否覆盖了低效条件(如status IN ('A','B')) - 临时锁住统计信息:
EXEC DBMS_STATS.LOCK_TABLE_STATS('SCHEMA','TABLE'),再手动改num_distinct测试执行计划是否切换 - 对比加索引前后
v$latch_children中cache buffers chains的spin_gets占比变化
别忽略 ASH 时间窗口和采样限制
v$active_session_history 是内存循环缓冲区,最大保留约 1 小时活动会话样本(取决于 SGA 大小和负载),而 dba_hist_active_sess_history 是每 10 秒采样一次的持久化表。两者都可能漏掉短时尖峰。
如果你看到某段时间 CPU 突高但 ASH 里 ON CPU 样本稀疏,很可能是因为采样间隙错过关键时段,或者会话在采样瞬间恰好处于空闲状态。
- 高并发场景下,优先用
v$active_session_history实时查(延迟 ≤ 1 秒),别等 AWR 报告 - 生成 ASH 报告时,
duration参数单位是秒,不是分钟;输入30是 30 秒,不是半小时 - 报告默认输出为
.html,不写扩展名会变成.lst,影响可读性
真正难处理的不是单次 latch 争用,而是那种 p1 值来回跳变但始终落在同一组连续 block 的情况——它不像固定热块那么好识别,却更消耗 latch 资源,需要结合执行路径和数据分布交叉验证。











