awr中segments by global cache buffer busy是定位gc buffer busy wait根源最快方式,直接指出被跨实例争用的表或索引分区;重点看该列累计等待时间,排序靠前者即热块源头,且仅rac环境awr报告中存在。

直接看AWR里Segments by Global Cache Buffer Busy
这个视图是定位gc buffer busy wait根源最快的方式。它不依赖你猜SQL或查会话,而是直接告诉你哪张表、哪个索引分区在被跨实例争用。重点看Global Cache Buffer Busy列的累计等待时间,排序靠前的段基本就是热块源头。
注意:该统计只出现在RAC环境的AWR报告“Instance Activity Stats”之后的“Segment Statistics”章节里,单实例没有。如果报告里没出现,说明要么没开AWR快照,要么采样期间没触发足够多的GC交互。
- 常见误判:看到
gc buffer busy acquire就去查SQL执行计划——其实90%以上情况,问题不在SQL写法,而在数据物理分布 - 真实场景中,
SEGMENTS BY GLOBAL CACHE BUFFER BUSY里排第一的往往是流水表的主键索引,尤其是按时间+机构做的二级分区索引叶块 - 如果同一对象在多个节点都高频出现,说明不是局部热点,而是全局热点(比如所有节点都在往同一个索引叶块插入)
用gv$session定位正在卡住的会话和资源类型
gv$session能实时抓到卡在gc buffer busy wait上的会话,比AWR更及时,适合压测中边调边看。关键不是看event字段,而是盯住p1和p2——它们指向具体被争用的资源。
p1通常是file#或object_id,p2是block#或class#。结合dba_objects反查对象名:
SELECT owner, object_name, subobject_name, object_type FROM dba_objects WHERE object_id = &p1;
如果p2值是17或18,大概率是UNDO段头或UNDO块争用;如果是4,优先检查表段头(FREELIST或ASSM位图块)。
- 别只查
event = 'gc buffer busy acquire',gc buffer busy release同样重要——后者常被忽略,但代表释放方卡住了,可能因LMS进程CPU满或私网延迟 - 如果
state = 'WAITING'且sql_id为空,说明卡在GC消息层,还没走到SQL执行阶段 - 在19c中,
p3有时携带锁模式(如3=SS,5=SX),可辅助判断是读-读、读-写还是写-写冲突
区分acquire和release的根本差异
gc buffer busy acquire表示当前实例正试图从远端拿块,但远端还没给;gc buffer busy release表示当前实例已处理完块,却卡在把锁还回去的路上。两者根因完全不同,不能混着优化。
acquire高:说明请求端压力大或远端响应慢,优先排查网络延迟、LMS负载、目标块是否真热;release高:说明本机LMS进程忙不过来,或远端GES协调异常,甚至可能是Bug(如19c已知Bug 32541263影响release路径)。
- 查LMS状态:
SELECT inst_id, pid, spid, program FROM gv$process WHERE program LIKE '%LMS%';看CPU占用和SPID是否僵死 - 查私网延迟:
SELECT * FROM gv$sysstat WHERE name LIKE 'gc%latency%';关注gc cr block receive time和gc current block receive time的平均值,超500微秒就要警惕 - release类等待在AWR中常伴随
ges messages send time升高,这是GES层瓶颈的强信号
绕过gc buffer busy的物理设计要点
分析清楚后,修复动作往往不在SQL或参数,而在表/索引结构。核心思路是让并发写入尽量分散到不同块、不同实例,减少跨节点GC需求。
对流水类表,最有效的是:一级按时间范围分区(如RANGE (create_time)),二级按业务维度哈希分区(如HASH (org_id)),且哈希分区数必须是2的幂(如64),避免Oracle内部哈希冲突放大。
- 索引必须和表一样做两级分区,尤其主键索引——否则索引叶块仍会成为单点热块
- 禁用
FREELIST,改用ASSM,但要确保INITIAL和NEXT大小合理,避免小扩展引发段头争用(class#=4) - 如果业务允许,把高频插入的列默认值设为
SYS_GUID()或DBMS_RANDOM.STRING(),打散插入位置,比调pctfree更治本
真正难的不是查出哪块热,而是确认那块热是因为设计缺陷,还是因为某条SQL在错误时间批量刷了10万行进同一个分区——后者需要结合ASH抽样和SQL执行频次交叉验证,容易漏掉。











