gc current block busy 是 RAC 中因多节点争抢同一数据块当前版本(xcur)导致的跨节点等待事件,根本原因是持有方未及时释放块,引发连锁阻塞与热点扩散。
gc current block busy 是什么,它为什么卡住整个集群
这不是单个会话等一个块,而是多个节点在争抢同一数据块的当前版本(xcur state),且持有方长时间不释放。rac 中每个块的修改必须由持有 current 版本的实例串行化处理,其他节点想读最新值(构造 cr 块)或写入,都得排队等这个块“腾出来”。一旦某个事务更新热点行没提交、或 dml 频率过高导致 lms 进程忙不过来,gc current block busy 就会堆积,所有依赖该块的 sql(哪怕只是 select)都会被拖慢。
为什么等待会跨节点放大,而不是局部阻塞
RAC 的锁协调靠 GRD 和 LMS 进程完成,不是本地内存操作。当节点 2 等待节点 1 持有的 current 块时,LMS 必须跨私网发起请求、确认状态、转发块、回收 PI(Past Image)、更新 GRD 条目——每一步都引入延迟。如果私网带宽不足、丢包或 LMS 进程 CPU 占用高(v$process 中 LMS 状态为 ON CPU),这个链路就会变长。更麻烦的是,一个块被卡住后,依赖它的对象(比如索引叶块、父表主键块)也会连锁等待,形成“热点扩散”。
- 查
gv$session_wait中event = 'gc current block busy',看p1(file#)、p2(block#)是否高度集中 - 用
dbms_utility.data_block_address_file和dbms_utility.data_block_address_block解析 p1/p2,定位具体对象 - 查
gv$bh确认哪个 inst_id 当前持有该块且state = 'xcur' - 再查该实例上
gv$transaction和gv$session,找sql_id对应的未提交事务或长运行 DML
shared_pool flush 或 DDL 会意外加剧 gc current block busy
很多人以为清缓存能缓解等待,结果反而雪上加霜。ALTER SYSTEM FLUSH SHARED_POOL 在 RAC 下是广播操作,所有节点同时清空本地 shared pool,触发大量硬解析;而硬解析需要访问数据字典对象(如 obj$、tab$),这些对象本身就在少数数据块上,瞬间变成新的 global cache 热点。同理,CREATE OR REPLACE PACKAGE 这类 DDL 会先 drop 再 create,在中间窗口期,其他节点查询该包时既拿不到旧定义也等不到新定义,反复尝试获取 dictionary block 的 current 版本,直接推高 gc current block busy。
- 避免在业务高峰执行 DDL,尤其涉及高频调用的包、视图、同义词
- 改包体前先查
DBA_DEPENDENCIES,暂停上游应用,显式COMMIT后再操作 - 清理缓存用
DBMS_SHARED_POOL.PURGE精准驱逐,而非全局 flush - 确认 shared pool 是否真有压力:查
gv$shared_pool_advice,而不是凭感觉调大
db_cache_size 调大可能让 gc current block busy 更糟
增大 db_cache_size 不会减少跨节点块传输,反而可能让热点块在更多节点缓存中“多份存在”,增加 LMS 协调负担。尤其当 buffer cache hit ratio 已高于 90%,但 gc current blocks received 持续走高,说明写热点没解决,只是一味加缓存把问题从磁盘 I/O 转移到了 GC 网络开销上。真正要盯的是 gc current block busy 的平均等待时间(AWR 中 Time waited (microseconds)),超过 5ms 就值得干预,而不是看总等待次数。
- 查
gv$sysstat中gc current blocks received和gc cr blocks received的比值,若 > 1.5,说明写热点严重 - 检查是否启用了
_gc_policy_time等隐藏参数——它们对gc current block busy几乎无效,别乱调 - 优先考虑业务层拆分:比如把高频更新的计数器字段挪到单独小表,或用应用层缓存替代频繁 update


















