根本原因不是“锁太多”,而是多个会话高频访问同一热点数据块时争抢同一个cbc闩锁,该闩锁保护buffer cache中hash bucket下的链表;争用表现为自旋消耗cpu、latch: cache buffers chains等待事件高发,需结合ash和dba定位具体热点块及对象,并通过分散访问、调整缓存策略或优化执行计划缓解。
根本原因不是“锁太多”,而是访问热点数据块时,多个会话反复争抢同一个 cbc 闩锁——它保护的是 buffer cache 中某个 hash bucket 下的链表(chain),而这个链表正被高频访问或修改。
cbc latch 争用本质是内存结构访问冲突
CBC(cache buffers chains)闩锁不是传统意义上的锁,它是 Oracle 为保护 Buffer Cache 中每个 Hash Bucket 对应的链表而设的轻量级同步机制。当多个会话同时访问同一数据块(比如频繁查询/更新某张小表的主键记录),它们都会映射到同一个 Hash Bucket,进而竞争同一个 CBC 闩锁。
关键点在于:CBC 闩锁持有时间极短(微秒级),但一旦出现争用,会话会先自旋(spin)——在 CPU 上空转检查是否释放,这直接把 CPU 拉高;自旋失败后才进入 sleep 等待,此时表现为 latch: cache buffers chains 等待事件。
- 常见触发场景:
SELECT COUNT(*) FROM small_lookup_table被大量并发执行;高频UPDATE同一热点行(如订单状态表的“处理中”标志) - P1 值(闩锁地址)相同 + P2 值(闩锁编号)集中,说明争用集中在少数几个 CBC 闩锁上
- AWR 中
latch: cache buffers chains在 Top 5 等待事件里,且spin gets / gets比例 > 30%,基本确认是自旋消耗 CPU
如何定位具体是哪个数据块或对象引发争用
不能只看等待事件总数,得定位到被反复访问的物理块(DBA)。最有效路径是结合 v$active_session_history 和 dba_objects:
先查争用最集中的 DBA:
SELECT CURRENT_OBJ#, CURRENT_FILE#, CURRENT_BLOCK#, COUNT(*) c FROM v$active_session_history WHERE event = 'latch: cache buffers chains' AND sample_time > SYSDATE - 1/24 GROUP BY CURRENT_OBJ#, CURRENT_FILE#, CURRENT_BLOCK# ORDER BY c DESC FETCH FIRST 5 ROWS ONLY;
再反查对象名:
SELECT owner, object_name, object_type FROM dba_objects WHERE object_id = <current_obj>;</current_obj>
- 如果
CURRENT_OBJ#是 -1,说明争用来自 undo 块或 temp 段,需查v$rollstat或临时段使用情况 - 若对象是索引,尤其唯一索引的根块或分支块,极易成热点(如序列生成列上的索引)
- 注意区分:是逻辑读多(
consistent gets高)还是当前模式读多(db block gets高),前者常对应查询热点,后者更倾向 DML 热点
缓解 CBC 争用的实操手段
目标不是“消灭争用”,而是分散访问、减少单链表压力。Oracle 不提供直接调大 CBC 数量的参数,但可通过以下方式间接降低冲突概率:
- 增加
_db_block_hash_buckets(隐含参数,慎用):默认值通常为db_cache_size / 20左右,增大可稀释 Hash Bucket 冲突,但会增加内存开销和管理成本 - 对热点表/索引启用
ALTER TABLE ... CACHE:让其数据块尽量保留在 Buffer Cache 前端,减少链表遍历深度(注意:仅对全表扫描类访问有效) - 拆分单点更新:比如把“全局计数器”改成按业务维度分片(user_id % 100),写入不同行,避免所有更新挤在同一个块
- 避免在 PL/SQL 循环里做单行
SELECT ... FOR UPDATE,改用批量UPDATE ... WHERE IN或MERGE - 检查是否用了
/*+ INDEX */强制走小索引,结果导致大量逻辑读集中在索引根块——换成全表扫描或重建更大索引可能反而更优
容易被忽略的隐蔽因素
很多 DBA 查到热点对象就停步了,但实际问题可能藏在更底层:
- 统计信息陈旧导致优化器选错执行路径,把本该走分区剪枝的查询变成全索引扫描,所有会话都扫同一组叶子块
- 绑定变量窥探(bind peeking)失效,使 SQL 在不同谓词值下复用错误计划,偶然触发热点块访问
- 应用层连接池配置不当,比如最小连接数设为 0,高峰时瞬间创建数百连接,全部涌向同一热点逻辑
- Oracle 12c+ 的自适应执行计划可能在运行时切换为嵌套循环,而驱动表结果集很小,但被驱动表连接列无索引——所有匹配操作都落到同一数据块上
真正难的不是定位 CBC 等待,而是判断:这个争用是业务模型固有缺陷,还是执行计划偶然劣化,抑或底层存储布局(如索引组织表 IOT 的主键聚集特性)天然带来的访问倾斜。不结合业务语义看 DBA,等于只看了半张图。











