GC等待的本质是跨节点争用同一数据块引发的资源竞争,并非网络延迟所致;gc buffer busy acquire等待的是其他会话释放数据块锁,常见于热点块并发访问、未绑定变量SQL以及非本地master节点执行全表扫描等典型场景。
首先要明确一点:Cache Fusion本身并非问题根源,它只是将应用或数据访问模式中的潜在缺陷暴露了出来。GC等待居高不下,本质上就是多个节点之间争夺同一数据块所致。

gc buffer busy acquire 等待事件究竟在等待什么
这个等待事件,等待的是其他会话释放对某个数据块的持有锁,而非网络传输环节的延迟。举个例子,实例1的会话A正在更新某行数据(持有 XCUR 锁),实例2的会话B同时尝试读取这行数据(需要构造 CR 块),但如果发现该块正在被修改且尚未提交,会话B就会卡在 gc buffer busy acquire 上无法继续。
常见的触发场景通常包括:
- 热点主键或索引块被多个实例频繁并发访问(例如订单号序列、状态字段更新等场景)
- 未绑定变量的循环 INSERT 或 UPDATE 操作,导致硬解析增加并引发缓存块反复迁移
- 全表扫描类 SQL 在非本地 master 实例上执行,强制触发大量
gc cr block busy等待 - 应用未按照 RAC 架构的推荐方式来设计—本该分片存储的数据,却被集中写入单个表分区或索引
不要急于调整网络参数,先分析等待时间分布桶
通过查询 GV$SYSTEM_EVENT 中 gc cr block busy 和 gc current block busy 的等待时间分布,能够获得最直观的判断依据:
- 如果 16ms 桶的占比突然升高,那多半是真实的网络抖动或中断问题(建议检查
ethtool -S ethX中rx_discards是否非零) - 如果等待集中在 1ms 或 4ms 桶,那就不是网络层面的问题,而是锁争用或 SQL 访问模式导致的内部竞争
- 有一点需要特别注意:
ifconfig显示的 RX/TX 字节数包含了以太网头部开销,不能直接用于计算 GC 的流量利用率;真正应该关注的是 TCP 层的有效载荷和丢包指标
定位 GC 密集型 SQL 的三步法
无需依靠猜测,用数据说话才是最佳方式:
- 查询
GV$SQL中buffer_gets/executions较高但disk_reads/executions较低的语句—这类 SQL 在单实例环境中运行流畅,一旦迁移到 RAC 就极易成为 GC 性能杀手 - 针对可疑会话启用
ALTER SYSTEM SET EVENTS '10046 trace name context forever, level 8',通过抓取 trace 信息查看WAIT #1: nam='gc current block 2-way'后面关联的是哪条 SQL 语句 - 结合
GV$SEGMENT_STATISTICS查询global_cache_cr_blocks_served较高的对象,再反查其 master 节点(通过x$kjbr中的KJBRMASTER),确认是否与访问节点存在错位
能起效的隐式参数,并非越多越好
Oracle 官方虽然未公开推荐这些参数,但在生产环境中经过验证确实有效的包括:
_gc_affinity_time = 10:让数据块在本地多停留10秒,减少不必要的迁移操作;适用于读多写少、热点相对稳定的业务场景_gc_affinity_percent = 50:仅当本地访问占比低于50%时才考虑将块迁走,避免出现"刚热起来就被踢走"的反复迁移情况_gc_read_mostly_locking = TRUE:仅对显式标记为 READ ONLY 的表空间或分区生效,能够显著降低 GES 协调带来的开销- 谨慎调整
_gc_lms_processes:默认值为2,设为4在某些场景下可能有效,但超过4容易引发 LMS 进程之间的内部竞争,反而导致 CPU 消耗上升
最容易被忽略的一点是:GC 等待从来不是孤立存在的现象—它往往与 enq: TX - contention、buffer busy waits 或 gc current grant 2-way 等事件串联出现。遇到其中一个等待事件,就需要顺着整个等待链深入排查,否则只调整表面参数毫无实际意义。
