暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

RAC性能分析 —— gc buffer busy acquire 等待事件深度解析【Oracle数据库分享--0x17】

Acdante 2026-05-05
62

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的四种拆分给你列清楚,这玩意儿面试的时候也常考:

    等待事件
    作用域
    通俗解释
    buffer busy waits
    单实例本地
    隔壁工位的人占着打印机,你只能等
    read by other session
    单实例本地
    有人正在从仓库搬货进办公室,你等着
    gc buffer busy acquire
    RAC跨实例
    你打电话要货,货在上海仓库正被别人处理着
    gc buffer busy release
    RAC跨实例
    你手里有货,北京那边等着你发货呢

    四、 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 六大场景对比总表

    场景
    典型症状
    根因
    影响程度
    排查优先级
    热点块争用
    acquire占比高,热点集中
    索引/序列/段头热点
    ⭐⭐⭐⭐⭐
    跨实例DML冲突
    release也高,长事务
    全局锁/行级锁争用
    ⭐⭐⭐⭐
    索引分裂
    单调递增索引,插入时
    最右叶子块争用
    ⭐⭐⭐
    全表扫描风暴
    报表时段突发
    大表跨实例扫描
    ⭐⭐⭐
    私网问题
    gc等待普遍偏高
    私网配置错误
    ⭐⭐⭐⭐⭐
    最高
    参数与Bug
    版本特定,突发
    Oracle Bug/参数
    ⭐⭐

    六、 诊断方法论:从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) rn 
      FROM (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) pct 
      FROM dba_hist_system_event h 
      WHERE h.event_name 
      IN ('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 s  
        WHERE s.event 
        IN ('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 d  
          WHERE e.file_id = d.file_id  
          AND 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_us 
            FROM gv$session_wait_history 
            WHERE event IN 
            ('gc buffer busy acquire','gc buffer busy release'
            GROUP BY inst_id, p1, p2 
            ORDER BY COUNT(*
            DESC FETCH FIRST 20 ROWS ONLY;

            6.6 SQL溯源与事务关联

            找到热点对象后,还需要关联到具体的SQL和事务:

            -- 关联等待会话的SQL和事务信息
              SELECT 
              s.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_STATUS
              FROM gv$session s, gv$transaction t
              WHERE 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全局缓存负载诊断

              看看全局缓存的工作负载分布:

              -- =============================================
              -- RAC全局缓存统计信息
              -- =============================================
                SELECT inst_id, NAME, VALUE, ROUND(VALUE / SUM(VALUEOVER() * 1002) pct
                FROM gv$sysstat
                WHERE 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, SOURCE
                  FROM gv$cluster_interconnects
                  ORDERBY 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) * 1002) pct 
                    FROM gv$system_event 
                    WHERE 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行为在不同版本之间有不少差异,了解这些差异能帮你更准确地判断问题:

                    版本
                    主要变化
                    gc等待事件特点
                    11g R2
                    引入更多gc事件细分
                    gc buffer busy比较普遍,_gc_policy_time默认为10
                    12c R1
                    引入Sharding概念,增强缓存融合
                    引入gc application waiting,优化了部分等待场景
                    12c R2
                    PDB级别缓存融合
                    支持更细粒度的资源隔离
                    18c
                    自动索引、内存管理增强
                    更智能的热点数据放置
                    19c
                    自动DDL、实时统计信息
                    自动索引可能加剧热点块的争用
                    23ai
                    JSON支持增强、AI向量索引
                    RDMA支持改善私网传输,降低gc等待

                    7.2 各版本已知Bug速查

                    有几个关键的隐藏参数在不同版本里的默认值不一样,你得注意:

                    参数名
                    作用
                    11g默认值
                    19c默认值
                    _gc_policy_time
                    GCS策略评估间隔
                    10
                    可能不同
                    _fairness_threshold
                    锁公平性阈值
                    5000
                    5000
                    _gc_read_mostly
                    读优化策略
                    FALSE
                    TRUE
                    _gc_undo_affinity
                    Undo块亲和性
                    FALSE
                    TRUE

                    ⚠️ 警告:隐藏参数不建议随意修改,除非你有MOS文档明确支持。在修改之前,请务必做好备份和测试。这玩意儿动不好轻则性能下降,重则数据库起不来!

                    八、 解决方案武器库

                    8.1 应用层优化

                    业务分区/服务配置
                    这是解决跨实例争用的根本方法。Oracle RAC支持通过服务(Service)来实现工作负载的隔离:

                      -- =============================================
                      -- 创建服务实现业务分区
                      -- =============================================
                      BEGIN
                         DBMS_SERVICE.CREATE_SERVICE(     service_name     => 
                      'app_module_a'
                      ,     network_name     => 
                      'app_module_a'
                         );
                      END
                      ;
                      -- 将服务绑定到特定实例
                      BEGIN
                         DBMS_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_number  
                        FROM 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 1500(标准帧)
                          MTU 9000(巨帧)
                          16KB数据块分包
                          约12个包
                          仅2个包
                          包头开销占比
                          约3.3%
                          约0.6%
                          CPU中断频率
                          低(约1/6)
                          有效吞吐量
                          基准
                          提升5-10%
                          兼容性
                          ✅ 所有设备
                          ⚠️ 需全链路支持

                          ⚠️ 重要提醒:巨帧必须全链路配置!从服务器网卡→交换机端口→对端交换机端口→对端服务器网卡,整条链路上所有设备的MTU必须同时设置为9000(或相同的大MTU值)。任何一段不匹配都会导致丢包!

                          交换机侧配置

                          Cisco交换机配置示例:

                            ! Cisco交换机 - 全局启用巨帧
                            system jumbomtu 9000
                            ! 进入接口配置
                            configure terminal
                            interface GigabitEthernet1/0/1
                            ! 启用巨帧
                            mtu 9000
                            ! 保存配置
                            end
                            write memory
                            ! 验证配置
                            show interface GigabitEthernet1/0/1 | include MTU
                            ! 输出应显示: MTU 9000 bytes

                            华为/H3C交换机配置示例:

                              # 华为交换机 - 全局和接口配置
                              system-view
                              jumboframe enable 9000
                              interface GigabitEthernet0/0/1
                              jumboframe enable 9000
                              quit
                              # 验证
                              display interface GigabitEthernet0/0/1
                              # =============================================
                              # H3C交换机配置
                              # =============================================
                              system-view
                              interface GigabitEthernet1/0/1
                              port link-type hybrid
                              qos standard mtu 9000
                              quit
                              display 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 9000
                                ip link show eth1
                                # 查看所有网卡MTU
                                ip link show | grep -E "mtu|MTU"
                                # Bond接口配置巨帧(需要同时改bond和物理口)
                                ip link set bond1 mtu 9000
                                # 注意:bond下的slave物理口也要改MTU!
                                ip link set eth0 mtu 9000
                                ip 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
                                  # 修改连接MTU
                                  nmcli connection modify eth1 mtu 9000
                                  # 或者编辑配置文件
                                  vim etc/sysconfig/network-scripts/ifcfg-eth1
                                  # 添加:MTU=9000
                                  # 重新加载并重启连接
                                  nmcli connection down eth1
                                  nmcli connection up eth1
                                  # 验证
                                  ip link show eth1

                                  验证巨帧生效

                                    # 1. 本地验证:查看网卡MTU
                                    ip link show eth1
                                    # 输出示例: mtu 9000 qdisc mq state UP
                                    # 2. 查看网络统计
                                    netstat -i
                                    # 注意Ierrs/IgDrop等列是否有丢包
                                    # 3. 跨节点验证:ping大包测试(关键!)
                                    # -M do: 禁止分片,-s 8972: 8972+28(ICMP头)=9000
                                    ping -M do -s 8972 <节点2私网IP>
                                    # 如果ping不通?说明链路中某处不支持巨帧!
                                    # 检查顺序:服务器eth1 -> 交换机1 -> 交换机2 -> 服务器eth1
                                    # 4. 使用tracepath验证路径MTU
                                    tracepath <节点2私网IP>
                                    # 5. Oracle数据库验证
                                    grep -i mtu $ORACLE_HOME/network/admin/listener.ora
                                    oerr

                                    🔴 巨帧配置常见坑汇总:
                                    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参数

                                    参数 
                                    最小值
                                    Oracle建议值
                                    说明
                                    net.core.rmem_default
                                    262144
                                    4194304
                                    默认接收缓冲区(4MB)
                                    net.core.rmem_max
                                    262144
                                    16777216
                                    最大接收缓冲区(16MB)
                                    net.core.wmem_default
                                    262144
                                    262144
                                    默认发送缓冲区
                                    net.core.wmem_max
                                    262144
                                    16777216
                                    最大发送缓冲区(16MB)
                                    net.ipv4.udp_rmem_min
                                    4096
                                    4096
                                    UDP最小接收缓冲区
                                    net.ipv4.udp_wmem_min
                                    4096
                                    4096
                                    UDP最小发送缓冲区

                                    /etc/sysctl.conf 完整配置:

                                      # Oracle RAC UDP Buffer优化配置
                                      # 追加到 etc/sysctl.conf 文件末尾
                                      # =============================================
                                      # 网络核心参数 - UDP Buffer优化
                                      # =============================================
                                      net.core.rmem_default = 4194304
                                      net.core.rmem_max = 16777216
                                      net.core.wmem_default = 262144
                                      net.core.wmem_max = 16777216
                                      # UDP专用参数
                                      net.ipv4.udp_rmem_min = 4096
                                      net.ipv4.udp_wmem_min = 4096
                                      # 附加网络优化(可选但推荐)
                                      net.core.netdev_max_backlog = 30000
                                      net.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 = 4194304
                                        net.core.rmem_max = 16777216
                                        net.core.wmem_default = 262144
                                        net.core.wmem_max = 16777216
                                        net.ipv4.udp_rmem_min = 4096
                                        net.ipv4.udp_wmem_min = 4096
                                        net.core.netdev_max_backlog = 30000
                                        net.core.somaxconn = 1024
                                        EOF
                                          # 加载配置(按文件名数字顺序加载)
                                          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_default
                                              cat /proc/sys/net/core/rmem_max
                                              cat /proc/sys/net/core/wmem_default
                                              cat /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 解决方案速查表

                                                    问题类型
                                                    推荐方案
                                                    难度
                                                    预期效果
                                                    热点块争用
                                                    Sequence缓存↑ / 反向索引
                                                    ⭐ 低
                                                    显著
                                                    跨实例DML冲突
                                                    服务分区 / 应用层改造
                                                    ⭐⭐ 中
                                                    显著
                                                    索引分裂
                                                    哈希分区索引 / Local索引
                                                    ⭐⭐ 中
                                                    显著
                                                    私网问题
                                                    Jumbo Frame / RDMA / 检查GPnP
                                                    ⭐⭐⭐ 高
                                                    极显著
                                                    参数/Bug
                                                    打补丁 / 调整隐藏参数
                                                    ⭐⭐⭐ 高
                                                    不确定

                                                    九、 真实案例: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)初始化失败,私网配置无法正确识别,最后退化为公网路由。私网流量跑到了千兆公网上,延迟直接爆表!

                                                    解决方案

                                                    1. 停止所有数据库实例和CRS
                                                    2. 给GI Home补打同版本PSU
                                                    3. 重启GI(crsctl stop crs && crsctl start crs -all)
                                                    4. 验证私网配置恢复正常(IS_PUBLIC=FALSE)
                                                    5. 启动数据库,问题解决!

                                                    ✨ 修复效果

                                                    修复后再次查看AWR,gc buffer busy acquire直接降到0.3%以下,客户跑批量任务再也不卡了。

                                                    📌 GC等待排查核心经验:

                                                    1. 给RAC打PSU的时候,GI Home和DB Home必须同时打,而且版本和patch level必须一致!
                                                    2. 私网IS_PUBLIC必须为FALSE,如果显示TRUE说明私网配置有问题
                                                    3. gc buffer busy高企时,优先检查私网配置,再查应用SQL
                                                    4. 隐藏参数修改前必须有MOS文档支持,否则后果自负
                                                    5. 多节点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数据库监听机制深度解析-Listener解密【Oracle数据库分享--0x07】
                                                        Oracle19c-最新补丁19.30别着急更新有BUG已被抛弃-最新19.31已发布【Oracle技术分享-0x08】
                                                        Oracle数据库等级保护(三级)安全配置实战指南-用户密码策略、审计和Linux系统账户安全配置实战操作【Oracle数据库分享--0x09
                                                        Oracle数据快速恢复与还原实战指南:闪回功能与expdp逻辑备份【Oracle数据库分享--0x10】
                                                        Oracle数据库日志体系-诊断和排查问题第一步-找到故障表现以及如何联动排查实战从11g-26ai【Oracle数据库分享--0x11】
                                                        Oracle数据库节前巡检脚本分享——基础Shell巡检实践&自动化DB巡检系统分享【Oracle数据库分享--0x12】
                                                        Oracle ADG故障处理指南ORA-01111:数据文件名未知的紧急恢复实战【Oracle数据库分享--0x13】
                                                        Oracle RAC 三节点集群在线替换 ASM 磁盘组底层存储实战【Oracle数据库分享--0x14】
                                                        Oracle容灾平台引发的思考-Acdante四层云原生容灾平台架构构思【架构构思分享--0x1】

                                                        Oracle 26ai数据库架构体系结构与新特性浅显分享【Oracle数据库分享--0x15】

                                                        Oracle 8i 迁移到11g-exp/imp 实战记录引发的六大数据库迁移方式深度对比分享【Oracle数据库分享--0x16】

                                                        文章转载自Acdante,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                                        评论