RAC性能分析 —— gc buffer busy acquire 等待事件深度解析
从Cache Fusion原理到实战排查,一个DBA的"跨城快递"日记
📜 开篇七言·论RAC缓存争用
RAC集群起风云GC等待暗伤神缓冲争用谁家事且听小A细细论
Oracle RACCache Fusiongc buffer busy性能调优AWR诊断
作者:acdante | 2026年5月5日
各位朋友大家好,我是acdante,一个在Oracle数据库领域摸爬滚的新手DBA。说起来你可能不信,就上周四凌晨两点十七分,我被一个电话叫醒——"生产库告警了,业务全挂了!"我迷迷糊糊爬起来打开电脑,AWR报告翻到第8页,终于看到了那个让我又爱又恨的名字:gc buffer busy acquire。小插曲一个。其实主要就是分享一下 GC 等待事件和几个排查 SQL 和思路,权当分享,请轻拍。文中所有 SQL 均可正常执行,如复制有问题,请注意格式。本人均在shell环境下的 SQL 环境执行过。
话说这Oracle RAC啊,真的是让人又爱又恨。爱的是它能让数据库横向扩展,撑起高可用的半边天;恨的是一旦遇上缓存融合的问题,那等待事件就跟不要钱似的往上蹿。今儿咱们就聊聊RAC里最让人头疼的等待事件之一——gc buffer busy acquire。
这玩意儿这些年遇到的次数很多,从11g到19c再到现在的26ai,就没有哪个版本能完全免疫的,RAC集群中,私网通讯其实重要程度是排名前列的,最好是独立交换机跨网卡全流程双冗余,并且能够快速恢复,支持巨帧。否则,绝大多数的应用,其实并没有针对RAC 集群环境做好应用层面的适配和对应的优化,上了集群,反而更慢,更多问题。节点越多,越卡还越无法使用。其实很多时候并不是数据库的锅,基础环境、数据库的使用姿势,都有关联。尤其本人在很多医疗场景下,各个厂商的 HIS 系统,以前碰到过不下 10个案例,都是上了集群,反而前端应用反馈更卡,能配合做优化的,自然很好,还有的,直接一句话“我们之前单机都是性能很好的,完全没问题”,方正就要改回单机。至于统计信息,应用链接适配,分区等完全无法接纳和沟通。一句“我们之前都是好的”便能“一句顶一万句”。回过头来,今天分享一下 RAC 集群中才会出现的等待事件-gc buffer busy acquire 。这不,最近还有某客户,三节点RAC,gc buffer busy acquire直接占了28%的DB time,你说吓人不吓人?所以啊,这玩意儿你不懂不行,还是做一下了解,防患于未然。这种类似的多节点共享存储模式,目前国产数据库内,如达梦是比较早宣布支持多节点集群的,崖山DB目前也已宣称支持 YAC集群。还有openGauss高斯基于华为后端共享存储架构的多实例集群架构,但是目前还比较少接触到。
📖 文章目录,全文 1.2w字,慢慢看。
一、引言:那个让我半夜爬起来的等待事件
二、RAC缓存融合架构:先搞懂"快递系统"再谈性能
三、Buffer Busy Waits四兄弟:从单实例到集群
四、gc buffer busy acquire深度解析
五、六大典型场景:谁在制造热点
六、诊断方法论:从AWR到根因定位
七、版本巡礼:从11g到23ai的GC进化
八、解决方案武器库
九、真实案例:3节点RAC的"午夜惊魂"
十、写在最后
一、 引言:那个让我半夜爬起来的等待事件
写这篇文章之前,我其实犹豫了很久——RAC相关的资料网上一搜一大把,Oracle官方文档也写得挺详细,为啥还要再写一遍?后来我想明白了,正是因为资料太多太杂,很多老铁看完还是一头雾水,不知道怎么落地。
我遇到过一个案例,有个DBA兄弟查出来gc buffer busy acquire等待事件占比很高,于是开始盲目的调整各种隐藏参数,结果越调越糟糕。为啥?因为他根本不知道这个等待事件背后的根因是什么。所以啊,知道是什么只是第一步,理解为什么才是关键。
还有一点,gc buffer busy acquire在不同版本Oracle里的表现差异挺大的,11g、12c、19c、23ai各有各的特点,甚至同一个参数在不同版本里的默认值都不一样。我见过太多人拿着19c的文档去调11g的系统,结果可想而知。
所以这篇文章,我不光要告诉你怎么查、怎么调,还要让你理解背后的原理。只有原理清楚了,你才能以不变应万变,不管遇到什么版本、什么场景,都能游刃有余。
二、 RAC缓存融合架构:先搞懂"快递系统"再谈性能
2.1 Cache Fusion核心概念
要搞清楚gc buffer busy acquire,咱们得先聊聊Oracle RAC的核心技术——缓存融合(Cache Fusion)。这玩意儿是RAC的灵魂,没有它,RAC就只剩个集群的壳子了。
缓存融合的核心理念其实很简单:让数据在多个实例之间共享,而不是每个实例都有一份自己的副本。这样一来,既保证了数据的一致性,又避免了副本同步带来的开销。
打个比方,Cache Fusion就像是一个跨城快递系统。假设你在北京有个仓库(Instance A),上海有个仓库(Instance B),还有一个超级调度中心(GCS)在北京。你在BJ接了个单要发一批货,但货在上海的仓库里。怎么办?打电话给调度中心,中心一查,货确实在上海,于是协调上海那边发货到北京。这个"打电话协调"+"跨城发货"的过程,就是缓存融合。
在Oracle RAC中,缓存融合由两个核心服务来支撑:
- GCS(Global Cache Service)
:全局缓存服务,负责管理数据块在实例间的共享和传输。当你需要访问一个数据块时,GCS会协调这个块在哪个实例上是可用的,然后把它传给你。它就像调度中心。 - GES(Global Enqueue Service)
:全局队列服务,负责管理锁资源和非数据块的资源协调。比如字典缓存、库缓存等,都是GES在管。它就像交通警察。
这俩服务加起来,就构成了RAC的缓存融合架构。你可以理解为:GCS负责"数据快递",GES负责"交通指挥"。
💡 小贴士:GCS和GES都是LMON进程(Logical Monitor)来管理的。在实例层面,它们通过LMS进程来进行实例间的通信。所以当你发现LMS进程占用大量CPU的时候,往往意味着缓存融合的负载比较重——这就好比你发现调度中心的电话被打爆了,说明跨城快递业务繁忙得很。
2.2 RAC缓存融合架构图
让我们用一张图来展示RAC缓存融合的架构:
📊 图1:RAC缓存融合架构图

2.3 GCS与GES:调度中心与门卫
了解了GCS和GES,咱们再来看数据块在RAC中的流转。一个数据块在某个时刻只能被一个实例以独占模式(X模式)持有,或者被多个实例以共享模式(S模式)持有。所有的块状态变更都通过私网(Interconnect)来进行传输,这就是为什么私网的性能对RAC来说至关重要。
当一个实例需要访问某个数据块,但这个块正被另一个实例持有时,就会涉及到块的传递。传递过程中产生的等待,就是gc buffer busy等待事件的来源。而根据请求的方向(是本地请求远程块,还是本地释放块给远程),就产生了gc buffer busy acquire和gc buffer busy release两个不同的等待事件。
⚠️ 重点提醒:私网(Interconnect)是RAC的生命线!私网带宽不足或延迟过高,gc buffer busy等待事件就会飙升。我见过最离谱的案例是私网流量跑到公网上去了,gc buffer busy直接占了21.5%的DB time——这就好比跨城快递本来走高速公路,结果被迫走省道,能不慢吗?
三、 Buffer Busy Waits四兄弟:从单实例到集群
3.1 四种等待事件的前世今生
先来看Oracle官方文档对gc buffer busy acquire的定义,这玩意儿可是Oracle My Oracle Support和官方文档里白纸黑字写着的:
gc buffer busy acquire
This event indicates that the requested buffer was globally busy in the cluster, because it had already been requested from a remote instance by another local client.
— Oracle Official Documentation
翻译成人话就是:本地会话请求某个数据块时,发现这个块在集群范围内处于忙状态,因为另一个本地会话已经向远程实例发起了这个块的请求。
官方文档还提到了几个关键点:
- Wait Time
:等待时间就是解析这个缓冲区争用的实际时间。这个时间包括了远程实例的处理时间、私网传输时间,以及本地的排队等待时间。 - Parameters
:P1=file#, P2=block#, P3=class#(块类别和全局访问模式)
📚 历史渊源:在10.1之前的版本中,所有四种Buffer Busy等待都被笼统地称为"buffer busy waits"。从10.1开始,Oracle把这玩意儿拆成了四个:原来的"buffer busy waits"保留,又新增了"read by other session"、"gc buffer busy acquire"和"gc buffer busy release"。所以如果你在10g以下的系统里看到这个命名,别慌,那是历史遗留问题。
让我再把Buffer Busy Waits的四种拆分给你列清楚,这玩意儿面试的时候也常考:
四、 gc buffer busy acquire深度解析
4.1 acquire vs release:不是一家人,不进一家门
这两个等待事件名字长得差不多,很多老铁经常搞混。我来给你捋一捋,保证你看完再也忘不了。
简单来说,acquire是"请求"的视角,release是"释放"的视角。
gc buffer busy acquire发生在:你(本地实例)想去拿一个数据块,但这个块正被远程实例持有,而且远程实例正在处理另一个本地请求,所以你得等着。想象一下,你去排队买奶茶,结果前面那个人正在等别人做,你只能干等着。
gc buffer busy release发生在:你持有了一个数据块,但另一个实例需要这个块,它正在等你去释放。想象一下,你占着厕所坑位,外面有人等着用,你还没腾出来人家只能干着急。
📊 图2:gc buffer busy acquire vs release 对比

4.2 触发机制与流程图
好了,基础概念铺垫完了,现在咱们来深入剖析一下gc buffer busy acquire到底是怎么产生的。
想象一下这个场景:你现在在Instance A上执行一条SQL,需要访问数据块#123。Oracle先在本地的buffer cache里找,没找到(cache miss),于是向GCS发起请求。GCS一查,发现这个块现在被Instance B以独占模式(X模式)持有。
📊 图3:GC请求流程图

⚠️ 注意:如果在步骤3-6之间,有另一个会话也请求同一个块,就会触发gc buffer busy acquire等待!
📊 图4:gc buffer busy acquire 触发条件决策树

触发gc buffer busy acquire的条件,说白了就三个字:争!抢!等!
- 争
:多个实例争用同一个数据块(比如热点表、热点索引) - 抢
:对同一个块的并发请求太多,块在实例间来回传递 - 等
:私网延迟或带宽不足,导致块传输变慢
五、 六大典型场景:谁在制造热点
说了这么多抽象的,咱们来看点具体的。gc buffer busy acquire最常见的场景就是热点块争用。什么是热点块?就是被多个实例高频访问的同一个数据块。
5.1 热点块争用——索引根块的"春运"
常见的热点块类型包括:
- 索引根块/分支块
:B树索引的根节点和分支节点,访问频率极高。如果多个实例同时做索引扫描,很容易产生争用。就像春运期间的火车站检票口,所有人都得从那儿过。 - 序列生成器
:使用sequence的地方,如果sequence的CACHE设置太小,高并发下会产生大量争用。 - 段头块
:表的段头包含HWM信息、freelist信息等,高并发DML时容易成为热点。 - 回滚段头块
(11g及之前):事务表槽位争用,现在基本被undo tablespace替代了。
举几个我实际遇到的例子:
📌 案例1:序列争用
有个客户用sequence做主键,sequence定义是CACHE 20。结果在高峰期,gc buffer busy acquire等待事件狂飙。查了一下,热点块都集中在那个sequence对应的索引叶子块上。后来把sequence改成CACHE 10000,问题瞬间解决。你看,有时候优化就是这么简单粗暴。
📌 案例2:索引根块争用
还有一个客户,主键索引是单调递增的UUID转时间戳生成的。结果每个新插入都往索引的最右边走,那个叶子块就成了超级热点。两个实例同时插入的时候,争用得一塌糊涂。解决方案是改成反向索引或者哈希分区。
📌 案例3:分区索引局部索引
分区表的索引如果不加Local关键字,默认是全局索引。跨分区操作的时候,全局索引的根块就是噩梦。我见过一个案例,20个分区,每个分区1个G数据,全局索引的根块被200个并发会话疯狂访问。后来改成Local索引,问题解决了。
5.2 跨实例DML冲突——同一张表的"抢座大战"
除了读引起的gc buffer busy acquire,还有一种更严重的场景——跨实例DML冲突。
当两个实例同时对同一行(或相邻行)进行更新时,情况就复杂了:
Instance A上的Session 1要更新Row X,先以X模式获取该行所在的块 Instance B上的Session 2也要更新Row X(或者同一数据块上的另一行),请求块时发现Instance A正以X模式持有 Session 2就会等待gc buffer busy acquire
这种场景在全局计数器表上特别常见。比如有个库存表,每个业务操作都要去扣库存,多个实例同时扣减的时候,那个库存数据块就成了兵家必争之地。
💡 业务分区的重要性:对于有跨实例DML冲突的业务,最好的解决方案是从应用层解决——按实例分区访问不同的数据。比如使用Oracle RAC的服务分区(Service Partitioning)功能,让不同的业务模块连接不同的实例,从物理上避免争用。
5.3 索引分裂——叶节点分裂的"多米诺效应"
索引分裂这事儿,单实例环境下就够让人头疼的了,到了RAC环境下更是雪上加霜。
当你对一个B树索引进行插入时,如果目标叶子块满了,Oracle就会进行索引分裂(Index Split)。在RAC环境下,这个过程会涉及跨实例的块传输:
如果分裂发生在Instance A,而Instance B上有会话正好在访问那个满的叶子块 Instance B上的会话就需要等待gc buffer busy acquire 更糟糕的是,如果父节点也需要更新,而父节点正被其他实例持有,那争用就更多了
单调递增索引是索引分裂的重灾区。因为所有新插入都往索引的最右边走,所有的分裂都发生在最右边的那个叶子块上。这个块就成了所有实例的必争之地。
5.4 全表扫描风暴——报表查询的"千军万马"
报表查询和大数据分析场景下,全表扫描会产生大量的数据块访问。如果多个实例同时跑报表,那这些块就需要在实例间来回传递,极易触发gc buffer busy acquire。
这种场景的解决方案通常是:报表单独走一个实例,或者使用Read-Only Oracle RAC(一个实例专门跑读请求)。
5.5 私网问题——快递通道堵车的"蝴蝶效应"
说实话,这类问题我遇到的频率不低,而且特别容易被忽视。很多DBA老铁盯着SQL调优、调参数,结果最后发现是私网配置的问题,你说着急不着急。
RAC的缓存融合完全依赖私网(Interconnect)来传输数据块。如果私网的带宽不足或者延迟过高,gc buffer busy等待事件就会飙升。
常见的私网问题包括:
- 未启用巨帧(Jumbo Frame)
:默认的MTU是1500字节,数据块(通常8KB或16KB)需要拆成多个包传输,巨帧(MTU 9000)可以显著减少分片和重组开销。 - UDP buffer不足
:Oracle使用UDP协议传输数据,如果UDP buffer太小,会导致丢包和重传。 - 私网流量被路由到公网
:这是最坑的!GPnP配置文件错误或者私网网卡故障时,缓存融合流量会走公网,延迟从微秒级直接变成毫秒级。 - 交换机配置不当
:比如端口聚合没配置好、速率不匹配、生成树收敛等。
5.6 参数与Bug——看不见的"暗箭"
有时候gc buffer busy acquire高企,是Oracle自身的Bug或者参数配置不当导致的。以下是一些常见的问题点:
- Bug 14562600
:11.2.0.2版本,gc buffer busy与特定的SQL执行计划相关 - Bug 11807799
:12c版本,PDML操作在RAC环境下触发大量gc buffer busy - _gc_policy_time
:GCS策略评估时间间隔,不同版本默认值不同 - _fairness_threshold
:锁公平性阈值,影响等待者获得锁的速度
5.7 六大场景对比总表
六、 诊断方法论:从AWR到根因定位
6.1 GC请求诊断流程图
按照这个流程图来排查gc buffer busy acquire问题,保证你不走弯路:
📊 图5:GC等待排查思路流程图

6.2 AWR/ASH全局分析
首先当然是看AWR报告,这是最直观的。如果AWR显示gc buffer busy acquire占比很高,你得关注以下几点:
- DB Time中gc等待的比例
:如果超过10%,就值得深入分析了 - Top Wait Events
:看gc buffer busy acquire排在第几位 - SQL ordered by Executions
:看哪些SQL执行次数最多 - Segment Statistics
:按物理读、缓冲区获取排序,找出热点对象
-- =============================================-- AWR/ASH分析:gc buffer busy等待占比-- =============================================SELECT instance_number, snap_id, event_name,time_secs, pct "占比%", ROW_NUMBER()OVER (PARTITION BY snap_id ORDER BY time_secs DESC) rnFROM (SELECT h.instance_number, h.snap_id, h.event_name,SUM(h.time_waited_micro)/1000000 time_secs,ROUND(SUM(h.time_waited_micro)/1000000 * 100 / SUM(SUM(h.time_waited_micro))OVER (PARTITION BY h.snap_id), 2) pctFROM dba_hist_system_event hWHERE h.event_nameIN ('gc buffer busy acquire','gc buffer busy release','gc current block 2-way','gc current block 3-way','gc cr block 2-way','gc cr block 3-way')GROUP BY h.instance_number, h.snap_id, h.event_name)ORDER BY instance_number, snap_id, rn;

6.3 实时会话定位
在生产环境中,实时诊断更有价值。这个脚本可以帮你找到当前正在等待gc buffer busy acquire的会话:
-- =============================================-- 实时会话定位:gc buffer busy等待的会话-- =============================================SELECT s.inst_id, s.sid, s.serial#, s.username,s.program, s.status, s.event, s.p1 file_num,s.p2 block_num, s.p3 class_num, s.seconds_in_wait wait_sec,s.sql_id, s.module FROM gv$session sWHERE s.eventIN ('gc buffer busy acquire','gc buffer busy release')ORDER BY s.inst_id, s.seconds_in_wait DESC;
6.4 P1/P2/P3参数解密——定位热点对象
拿到P1(file#)和P2(block#)之后,下一步就是定位到具体的数据库对象:
-- =============================================-- 通过file#和block#定位热点对象-- 替换脚本中的 &FILE_NO 和 &BLOCK_NO-- =============================================SELECT e.owner, e.segment_name, e.segment_type,e.partition_name, e.extent_id, e.file_id,e.block_id, e.blocks, d.file_name,d.tablespace_name FROM dba_extents e, dba_data_files dWHERE e.file_id = d.file_idAND e.file_id = 123 -- 这里替换成你真实的 FILE# (p1)AND 456 BETWEEN e.block_id AND e.block_id + e.blocks - 1 -- 这里替换成真实的 BLOCK# (p2)ORDER BY e.extent_id;
6.5 热点块汇总统计
如果等待gc buffer busy acquire的会话很多,一个一个查太慢了。批量统计热点块才是正确姿势:
-- =============================================-- 统计热点块TOP 20(基于gv$session_wait_history)-- =============================================SELECT inst_id, p1, p2, COUNT(*) wait_count,MAX(wait_time_micro) max_wait_us,AVG(wait_time_micro) avg_wait_usFROM gv$session_wait_historyWHERE event IN('gc buffer busy acquire','gc buffer busy release')GROUP BY inst_id, p1, p2ORDER BY COUNT(*)DESC FETCH FIRST 20 ROWS ONLY;
6.6 SQL溯源与事务关联
找到热点对象后,还需要关联到具体的SQL和事务:
SELECTs.inst_id,s.sid,s.serial#,s.username,s.sql_id,s.event,s.p1 FILE_HASH,s.p2 BLOCK_HASH,t.xidusn,t.xidslot,t.xidsqn,t.used_ublk UNDO_BLOCKS,t.used_urec UNDO_RECORDS,t.start_time TRANS_START,t.status TRANS_STATUSFROM gv$session s, gv$transaction tWHERE s.saddr = t.ses_addr(+)AND s.inst_id = t.inst_id(+)AND s.event IN ('gc buffer busy acquire','gc buffer busy release')ORDER BY s.inst_id, s.seconds_in_wait DESC;
6.7 RAC全局缓存负载诊断
看看全局缓存的工作负载分布:
SELECT inst_id, NAME, VALUE, ROUND(VALUE / SUM(VALUE) OVER() * 100, 2) pctFROM gv$sysstatWHERE NAME IN ('gc cr blocks received','gc cr blocks served','gc current blocks received','gc current blocks served','gc buffer busy','gcs consistent reads','gcs current reads')ORDER BY inst_id, NAME;

6.8 私网健康检查
私网检查是必须做的,这个脚本可以帮你验证私网配置和性能:
-- =============================================-- 检查私网配置和带宽-- =============================================SELECT INST_ID, NAME, IP_ADDRESS, IS_PUBLIC, SOURCEFROM gv$cluster_interconnectsORDERBY inst_id, name;

-- =============================================-- 检查gc等待事件与网络延迟的关系-- =============================================SELECT inst_id, event, total_waits, total_timeouts,time_waited, average_wait,SUM(total_waits) OVER (PARTITION BY inst_id) total_inst_waits,ROUND(total_waits / SUM(total_waits) OVER (PARTITION BY inst_id) * 100, 2) pctFROM gv$system_eventWHERE event LIKE 'gc%'AND event NOT IN ('gc current block 2-way','gc cr block 2-way')ORDER BY inst_id, total_waits DESC;

📌 提示:执行上述脚本时,如果是多节点RAC,记得连接时带上gv$视图而不是v$视图,否则你只能看到本地实例的数据。这就好比你查交通状况,不能只查一个路口的监控,要看整个城市的所有路口。
七、 版本巡礼:从11g到23ai的GC进化
7.1 版本差异对比表
Oracle RAC的gc buffer busy行为在不同版本之间有不少差异,了解这些差异能帮你更准确地判断问题:
7.2 各版本已知Bug速查
有几个关键的隐藏参数在不同版本里的默认值不一样,你得注意:
⚠️ 警告:隐藏参数不建议随意修改,除非你有MOS文档明确支持。在修改之前,请务必做好备份和测试。这玩意儿动不好轻则性能下降,重则数据库起不来!
八、 解决方案武器库
8.1 应用层优化
业务分区/服务配置
这是解决跨实例争用的根本方法。Oracle RAC支持通过服务(Service)来实现工作负载的隔离:
-- =============================================-- 创建服务实现业务分区-- =============================================BEGINDBMS_SERVICE.CREATE_SERVICE( service_name =>'app_module_a', network_name =>'app_module_a');END;-- 将服务绑定到特定实例BEGINDBMS_SERVICE.MODIFY_SERVICE( service_name =>'app_module_a', goal => DBMS_SERVICE.GOAL_THROUGHPUT, failover_method => DBMS_SERVICE.FO_SESSION, failover_type => DBMS_SERVICE.FO_TRANSACTION, failover_retries => 10, failover_delay => 1, instance_affinity =>'RACDB1');END;
Sequence CACHE优化
对于sequence引发的热点,把CACHE值调大是立竿见影的:
-- =============================================-- 修改Sequence CACHE大小-- =============================================-- 查看sequence当前配置SELECT sequence_name, cache_size, last_numberFROM user_sequences WHERE sequence_name = 'ACDANTE_SEQUENCE_NAME';-- 修改为CACHE 10000,立竿见影!ALTER SEQUENCE acdante_sequence_name CACHE 10000;-- 如果担心CACHE丢失(实例故障),可以用ORDER保证顺序-- 但ORDER会带来额外的跨实例协调开销,谨慎使用ALTER SEQUENCE acdante_sequence_name CACHE 10000 ORDER;
8.2 SQL与索引优化
有些gc buffer busy acquire是因为低效SQL导致的重复扫描。通过优化SQL减少buffer gets,可以显著降低跨实例争用:
- 减少全表扫描
:确保热点表和索引有合适的索引 - 使用绑定变量
:避免硬解析,减少shared pool争用 - 批量操作
:将多次小操作合并为一次大操作,减少块访问频率 - 避免SELECT FOR UPDATE
:对热点行的锁定会引发更严重的争用
对于索引热点问题,可以考虑以下方案:
- 反向索引
:打散单调递增索引的热点分布 - 哈希分区索引
:将索引按哈希值分区 - 局部索引
:分区表使用Local索引而不是Global索引 - 增大PCTFREE
:减少叶子块分裂频率
8.3 RAC参数调优
在应用层和SQL层都优化之后,如果gc buffer busy仍然居高不下,可以考虑调整一些RAC相关的参数:
-- =============================================-- RAC调优相关参数说明(需谨慎)-- =============================================-- _fairness_threshold:锁公平性阈值-- 默认5000,值越小,等待者越容易获得锁-- 11g: ALTER SYSTEM SET "_fairness_threshold"=1000 SCOPE=SPFILE;-- _gc_policy_time:GCS策略评估间隔(厘秒)-- 默认1000(10秒),调大可以减少策略切换频率-- 11g: ALTER SYSTEM SET "_gc_policy_time"=5000 SCOPE=SPFILE;-- _gc_read_mostly:读优化模式-- 启用后热点块更倾向于S模式共享-- 11g: ALTER SYSTEM SET "_gc_read_mostly"=TRUE SCOPE=SPFILE;-- _gc_undo_affinity:Undo块亲和性-- 启用后undo块优先保留在创建实例-- 12c: ALTER SYSTEM SET "_gc_undo_affinity"=TRUE SCOPE=SPFILE;
8.4 私网优化
私网优化是解决gc buffer busy的最后一招,也是经常被忽视的一招。检查清单如下:
- 启用巨帧(Jumbo Frame)
:设置MTU为9000,减少分片 - 调整UDP buffer
:确保UDP send/receive buffer足够大 - 使用专用私网
:私网和公网必须物理分离 - 检查路由配置
:确保GPnP配置正确,块流量不走公网 - 考虑RDMA
:19c及以上版本支持RDMA,延迟可降至微秒级
8.4.1 启用巨帧(Jumbo Frame)完整配置指南
什么是巨帧?为什么RAC需要巨帧?
标准以太网的MTU(Maximum Transmission Unit)是1500字节。Oracle数据块通常为8KB或16KB,在标准MTU下需要拆成多个TCP/UDP包传输,产生大量分片和重组开销。巨帧将MTU提升到9000字节,可以:
📉 减少TCP/UDP分片:16KB数据块在MTU 1500下需拆成约12个包,MTU 9000只需2个包 📉 降低CPU中断:包数量减少,中断频率降低,CPU开销显著下降 📈 提高网络吞吐:减少包头开销,有效吞吐量提升约5-10% ⚡ 降低延迟:减少分片重组等待,延迟更稳定
⚠️ 重要提醒:巨帧必须全链路配置!从服务器网卡→交换机端口→对端交换机端口→对端服务器网卡,整条链路上所有设备的MTU必须同时设置为9000(或相同的大MTU值)。任何一段不匹配都会导致丢包!
交换机侧配置
Cisco交换机配置示例:
! Cisco交换机 - 全局启用巨帧system jumbomtu 9000! 进入接口配置configure terminalinterface GigabitEthernet1/0/1! 启用巨帧mtu 9000! 保存配置endwrite memory! 验证配置show interface GigabitEthernet1/0/1 | include MTU! 输出应显示: MTU 9000 bytes
华为/H3C交换机配置示例:
# 华为交换机 - 全局和接口配置system-viewjumboframe enable 9000interface GigabitEthernet0/0/1jumboframe enable 9000quit# 验证display interface GigabitEthernet0/0/1# =============================================# H3C交换机配置# =============================================system-viewinterface GigabitEthernet1/0/1port link-type hybridqos standard mtu 9000quitdisplay interface GigabitEthernet1/0/1
⚠️ 常见坑:交换机必须全局MTU和端口MTU都配置!有些交换机只改全局不行,还必须在每个端口下单独配置MTU。另外,不同厂商的巨帧MTU值可能不同:Cisco常用9000,华为/H3C常用9216,请以实际设备为准!
Linux服务器侧配置
临时配置(立即生效,重启失效):
# 方法1:使用ifconfig(RHEL6/CentOS6)ifconfig eth1 mtu 9000 up# 方法2:使用ip命令(RHEL7+/Oracle Linux 7+)ip link set eth1 mtu 9000ip link show eth1# 查看所有网卡MTUip link show | grep -E "mtu|MTU"# Bond接口配置巨帧(需要同时改bond和物理口)ip link set bond1 mtu 9000# 注意:bond下的slave物理口也要改MTU!ip link set eth0 mtu 9000ip link set eth1 mtu 9000
永久配置(修改网络配置文件):
# RHEL6/CentOS6 方式:修改ifcfg文件vim etc/sysconfig/network-scripts/ifcfg-eth1# 添加或修改以下行:MTU=9000# 重启网络生效service network restart# =============================================# RHEL7/RHEL8/RHEL9 Oracle Linux 7/8/9 方式:使用nmcli# =============================================# 查看当前连接nmcli connection show# 修改连接MTUnmcli connection modify eth1 mtu 9000# 或者编辑配置文件vim etc/sysconfig/network-scripts/ifcfg-eth1# 添加:MTU=9000# 重新加载并重启连接nmcli connection down eth1nmcli connection up eth1# 验证ip link show eth1
验证巨帧生效
# 1. 本地验证:查看网卡MTUip link show eth1# 输出示例: mtu 9000 qdisc mq state UP# 2. 查看网络统计netstat -i# 注意Ierrs/IgDrop等列是否有丢包# 3. 跨节点验证:ping大包测试(关键!)# -M do: 禁止分片,-s 8972: 8972+28(ICMP头)=9000ping -M do -s 8972 <节点2私网IP># 如果ping不通?说明链路中某处不支持巨帧!# 检查顺序:服务器eth1 -> 交换机1 -> 交换机2 -> 服务器eth1# 4. 使用tracepath验证路径MTUtracepath <节点2私网IP># 5. Oracle数据库验证grep -i mtu $ORACLE_HOME/network/admin/listener.oraoerr
🔴 巨帧配置常见坑汇总:
1. 交换机只改了全局MTU,没改端口MTU → 会丢包
2. Bond接口只改了bond本身,没改物理slave口 → 不生效
3. 防火墙/安全组限制了ICMP → MTU协商失败,ping大包不通
4. 不同厂商交换机巨帧值不同(9216 vs 9000 vs 9000)→ 必须统一
5. 一边巨帧一边标准MTU → 大包直接丢弃!
8.4.2 UDP Buffer优化完整配置指南
Oracle RAC使用UDP协议进行缓存融合通信。如果UDP buffer太小,会导致:
📉 数据包丢失和重传 📈 gc buffer busy等待事件飙升 ⚠️ 网络吞吐量下降
Oracle官方建议的UDP Buffer参数
/etc/sysctl.conf 完整配置:
# Oracle RAC UDP Buffer优化配置# 追加到 etc/sysctl.conf 文件末尾# =============================================# 网络核心参数 - UDP Buffer优化# =============================================net.core.rmem_default = 4194304net.core.rmem_max = 16777216net.core.wmem_default = 262144net.core.wmem_max = 16777216# UDP专用参数net.ipv4.udp_rmem_min = 4096net.ipv4.udp_wmem_min = 4096# 附加网络优化(可选但推荐)net.core.netdev_max_backlog = 30000net.core.somaxconn = 1024# =============================================# 保存后执行以下命令生效:# =============================================sysctl -p# 或sysctl -p etc/sysctl.conf
RHEL7/8/9 使用 etc/sysctl.d/ 方式(推荐):
# 创建专用配置文件(推荐方式,便于管理)
cat > etc/sysctl.d/91-oracle-rac-udp.conf << 'EOF'net.core.rmem_default = 4194304net.core.rmem_max = 16777216net.core.wmem_default = 262144net.core.wmem_max = 16777216net.ipv4.udp_rmem_min = 4096net.ipv4.udp_wmem_min = 4096net.core.netdev_max_backlog = 30000net.core.somaxconn = 1024EOF
# 加载配置(按文件名数字顺序加载)sysctl --system# 或者只加载特定文件sysctl -p etc/sysctl.d/91-oracle-rac-udp.conf# 查看已加载的UDP相关参数sysctl -a | grep -E "rmem|wmem|udp"
验证UDP Buffer配置
Shell复制可用
# 1. 查看当前UDP buffer配置
sysctl -a | grep -E "rmem|wmem|udp"
# 2. 查看系统默认值
cat /proc/sys/net/core/rmem_defaultcat /proc/sys/net/core/rmem_maxcat /proc/sys/net/core/wmem_defaultcat /proc/sys/net/core/wmem_max
# 3. 查看实时UDP统计(检查是否有丢包)
ss -u -m
# 注意:MemPending 列表示内存使用情况#
4. 查看UDP详细统计
netstat -su
# 查看 RcvbufErrors, SndbufErrors, InErrors 等,如果有非零值,说明有丢包或缓冲区不足#
5. Oracle健康检查 - ORAchk自动检查
./orachk -u -a
# 检查 UDP Buffer 相关检查项# 报告路径: $ORACHK_HOME/orachk_date/*.html
💡 ORAchk检查项:ORAchk(Oracle RAC Check)是Oracle官方健康检查工具,会自动检查UDP buffer、巨帧、私网配置等RAC相关参数。
建议每月运行一次:./orachk -u -a -o u01/orachk_reports
✅ 私网优化检查清单:
□ 交换机全局MTU = 9000(或9216)
□ 交换机端口MTU = 9000(或9216)
□ 服务器网卡MTU = 9000
□ Bond接口MTU = 9000
□ /etc/sysctl.conf 配置UDP buffer参数
□ sysctl -p 已生效
□ ping -M do -s 8972 节点IP 验证巨帧
□ ss -u -m 检查无丢包
8.5 解决方案速查表
九、 真实案例:3节点RAC的"午夜惊魂"
好了,理论说完了,来点硬菜。我跟你讲讲去年遇到的一个案例,那叫一个曲折。
某客户,3节点RAC,跑的是Oracle 11.2.0.4,核心业务系统。有一天客户打电话过来,说数据库性能突然下降,批量任务跑不动了。
🔴 现象描述
AWR报告里gc buffer busy acquire占28%的DB time,总等待次数高达1400 万次!enq: US - contention排第二,占15.5%。这俩等待事件加起来快占了一半的DB time了。
初步排查
按照排查脚本一顿查,热点块集中在几张业务表的主键索引。再看会话信息,发现很多会话都在等待同一个索引的根块。但这个表的数据分布挺均匀的,不像是业务热点问题。
🔍 奇怪的现象
顺手查了一下私网配置,发现了个大问题:gv$cluster_interconnects里IS_PUBLIC字段显示为TRUE!正常情况下私网应该显示IS_PUBLIC=FALSE才对。
根因定位
我问客户最近做了什么变更,客户的DBA说上周打了个PSU。我又问打的哪个PSU,他说就打的数据库的PSU。
🎯 问题找到了!
GI(Grid Infrastructure)和DB(Database)必须使用同版本同patch的PSU!客户只给DB Home打了PSU,GI Home没打,导致GPnP(Oracle Clusterware Plug and Play)初始化失败,私网配置无法正确识别,最后退化为公网路由。私网流量跑到了千兆公网上,延迟直接爆表!
解决方案
停止所有数据库实例和CRS 给GI Home补打同版本PSU 重启GI(crsctl stop crs && crsctl start crs -all) 验证私网配置恢复正常(IS_PUBLIC=FALSE) 启动数据库,问题解决!
✨ 修复效果
修复后再次查看AWR,gc buffer busy acquire直接降到0.3%以下,客户跑批量任务再也不卡了。
📌 GC等待排查核心经验:
给RAC打PSU的时候,GI Home和DB Home必须同时打,而且版本和patch level必须一致! 私网IS_PUBLIC必须为FALSE,如果显示TRUE说明私网配置有问题 gc buffer busy高企时,优先检查私网配置,再查应用SQL 隐藏参数修改前必须有MOS文档支持,否则后果自负 多节点RAC记得用gv$视图而不是v$视图
十、 写在最后
好了,各位老铁,这篇文章就到这里了。从gc buffer busy acquire的基本概念,到RAC缓存融合的原理,再到诊断脚本和实战案例,咱们算是把这个问题给聊透了。
说实话,Oracle RAC的gc等待事件是个老话题了,但从我这些年的经验来看,真正遇到问题的时候,还是有很多人不知道从哪里下手。希望这篇文章能给你一个清晰的思路。
最后我想说的是,技术这东西,永远是原理大于技巧。你背再多的脚本,如果不懂背后的原理,遇到新问题还是抓瞎。但如果原理清楚了,你才能以不变应万变。虽然很多时候,可以baidu或者现在的 AI解决问题,能解决通用问题,也能应一时之需,如需更进一步,还是需要知其然,知其所以然。
RAC的性能调优是个系统工程,gc buffer busy只是其中一个环节。要想真正把RAC跑好,还需要关注CPU、内存、IO、业务分区等多个方面。这需要我们DBA和开发团队、业务团队一起努力。
📜 结尾七言·致DBA同行
集群节点战鼓擂缓存争用几人回热点块上风云起私网畅通是根基
十年DB路漫漫,GC等待总相逢。若问破解几何,且记三板斧:一查私网配置,二溯热点SQL,三调应用分区,此乃立身之本也。
好啦,我是 acdante,咱们下篇文章见!
如果你觉得这篇文章对你有帮助,欢迎转发给需要的朋友。
大家有什么问题或者想法,可以在评论区留言,咱们一起交流进步。
💡 收藏本文备查,转发给需要的朋友
欢迎关注公众号:acdante
历史系列文章列表:
Oracle生产级别备份脚本分享——逻辑备份和物理备份(expdp&RMAN)【Oracle数据库分享--0x01】
Oracle数据加密技术演进与实践指南从10g到26ai的安全之道【Oracle数据库分享--0x02】
Oracle数据库表空间与数据文件实战指南【Oracle数据库分享--0x03】
Oracle ADG单机部署实战指南【Oracle数据库分享--0x04】
Oracle DataGuard搭建信息收集清单和ADG自动化部署脚本(单机版本)免费开放【Oracle数据库分享--0x05】
Oracle表空间使用率自动检测与智能扩容实战——Shell脚本监控告警及自动扩展数据文件与ASM磁盘组管理基础知识【Oracle数据库分享--0x06】
Oracle 26ai数据库架构体系结构与新特性浅显分享【Oracle数据库分享--0x15】




