gc buffer busy acquire本质是远程块请求排队,即实例1等待实例2完成同块的CR/XCUR处理(如构造CR、刷redo、索引分裂或LMS卡顿),并非锁未释放或SQL错误所致。

gc buffer busy acquire本质是远程块请求排队
这不是“锁没释放”或“SQL写错”这种单实例思维能解释的问题。它明确表示:当前实例(比如实例1)想从**另一个实例(实例2)拿一个数据块的CR或XCUR版本**,但实例2那边还没处理完上一个同块请求——可能是正在构造CR、正在刷redo、正在分裂索引页,或者只是LMS进程被卡住。此时实例1就卡在gc buffer busy acquire上,等实例2腾出手来。
最常踩的三个坑:热点块、索引分裂、私网延迟
排查时别一上来就翻SQL执行计划。先看AWR里Segments by Global Cache Buffer Busy排名前三的对象,再结合Top SQL with Top Events交叉验证:
- 如果全是主键索引(尤其序列生成的ID),大概率是
index root/branch block争用,不是SQL慢,是结构热 - 如果等待集中在某张小表的全表扫描语句,说明多个实例在同时扫同一组block,没做分区或服务绑定
- 如果
Avg global cache cr block receive time (ms)> 5,且gc cr block flush time也高,问题不在应用层,而在实例2的LGWR或私网——哪怕ping延迟看起来正常,UDP丢包或Jumbo Frames没开也会让GC块传输卡在半路
_gc_policy_time 和 _fairness_threshold 参数容易被忽略
这两个隐含参数控制GCS对“块亲和性”的容忍度。默认值在高并发场景下往往太激进:
-
_gc_policy_time设得太小(如10),会导致块主节点频繁切换,每次切换都触发一次gc current grant+gc buffer busy acquire连锁等待 -
_fairness_threshold设得太高(如1000),会让GCS强行把访问分散到多个实例,反而把原本可本地缓存的块变成跨实例搬运 - 查当前值:
SELECT ksppinm, ksppstvl FROM x$ksppi JOIN x$ksppcv USING (indx) WHERE ksppinm IN ('_gc_policy_time', '_fairness_threshold');
v$wait_chains 能快速定位根因会话,但要注意时间窗口
用v$wait_chains找阻塞链比翻hanganalyze快得多,但必须在等待高峰时立即执行,否则链会断:
- 运行:
SELECT * FROM v$wait_chains WHERE state = 'WAITING' AND chain_signature LIKE '%gc buffer busy acquire%'; - 重点看
final_blocking_session和blocking_inst_id列——它指向的是真正持有块的会话,不是中间转发的LMS - 如果
blocking_inst_id和当前inst_id不同,说明是远程实例的某个DML事务没提交,或其buffer正在被LMS处理;如果相同,说明是本地实例的gc buffer busy release在上游堵住了
真正难处理的从来不是等待本身,而是当gc buffer busy acquire和gc cr block flush time、enq: TX - row lock contention混在一起时,你得判断哪一层最先卡住——网络、存储、LMS调度,还是应用逻辑。这时候AWR里每个毫秒级的平均值,都比SQL文本重要。


















