Oracle RAC集群通过多个实例共享同一套存储,为业务提供了高可用与横向扩展能力。但在实际运行中,很多故障并非出在集群本身,而是出在连接路由上:同一个业务模块的会话被负载均衡算法分散到不同实例,导致AQ队列表的块在实例之间频繁传递,DML语句遭遇gc buffer busy等待,队列消费者甚至读取不到自己刚入队的消息。这类问题的根源往往是Service配置不当。本文围绕RAC环境下Service、AQ队列与DML三者之间的关系展开分析,并给出可直接落地的配置方案。

一、为什么AQ队列在RAC中对Service配置格外敏感
高级队列(Advanced Queues,简称AQ)的队列表本质上是一组普通的堆表加上后台的队列机制。消息入队(ENQ)和出队(DEQ)都会对这些表的特定数据块加锁。在单实例数据库中,这些锁与缓冲区都在同一个SGA内,开销可控;而在RAC环境中,如果入队会话连接在节点一、出队会话连接在节点二,队列表的热块就必须在两个实例的缓冲区之间通过内部互联来回传递。
这种跨实例传递会带来两类典型问题。第一是性能问题:全局缓存等待事件如gc buffer busy acquire、gc buffer busy release会显著增加,队列吞吐量不升反降。第二是语义问题:AQ的出队默认基于提交后的可见性,如果生产者和消费者在不同实例上并发操作同一个队列,索引块争用会加剧,甚至出现消息延迟可见的现象,业务层表现为“消息丢了”,实际上只是还没提交可见。
因此,Oracle官方的建议是把AQ的生产者和消费者尽量固定在同一个实例上运行,实现方式就是创建一个专用的Service,并将其绑定到首选实例。这样所有通过该Service连接的会话都会落在同一个节点,队列表的热块不再跨节点流动,DML对队列表的更新也自然集中。
二、Service的负载均衡与故障切换属性对DML的影响
Service在RAC中决定了“连接去哪个实例”。与DML关系最密切的属性有两个:clb_goal(连接负载均衡目标)和failover(故障切换模式,即TAF属性)。
clb_goal有两个取值:DBMS_SERVICE.CLB_GOAL_THROUGHPUT表示按吞吐量均衡,适合长连接;CLB_GOAL_SHORT表示按连接数均衡,适合短连接频繁建立的OLTP系统。对于AQ这类需要会话亲和性的场景,光设置clb_goal还不够,因为负载均衡仍然可能把会话分到不同节点。真正起作用的是在OCR中为Service指定-preferred(首选实例)和-available(备用实例),配合srvctl命令让Service默认只在首选实例上运行。
TAF属性则决定了实例故障时会话的行为。对于普通DML,BASIC模式的FAILOVER会重新建立连接但不会自动重放未提交事务,应用层必须处理事务级重试;而PRECONNECT会在备用实例上预先建立连接,代价是占用双份资源。对于AQ消费程序,一般建议使用BASIC加SESSION级别的切换,让应用捕获错误后重新出队,因为队列消息本身是持久化的,实例重启后消息不会丢失。
-- 查看当前Service的配置情况
SELECT name, failover_method, failover_type, failover_retries,
clb_goal, goal
FROM dba_services
WHERE name LIKE '%aq%';
-- 查看Service在集群各实例上的运行状态
SELECT inst_id, name FROM gv$active_services ORDER BY name, inst_id;如果查询结果显示AQ业务使用的Service在多个实例上都处于运行状态,而业务连接串又没有指定INSTANCE_NAME,基本可以断定队列操作正在跨实例执行,这就是问题所在。
三、创建专用Service并绑定实例的完整步骤
下面以两节点RAC(实例orcl1、orcl2)为例,创建一个名为aq_svc的专用Service,首选实例为orcl1,可用实例为orcl2。整个过程分三步:用srvctl在集群层注册Service、启动Service、配置客户端连接串。
第一步,用grid用户执行srvctl命令完成注册并启动:
# 注册服务:首选实例orcl1,可用实例orcl2 srvctl add service -db orcl -service aq_svc \ -preferred orcl1 -available orcl2 # 启动服务 srvctl start service -db orcl -service aq_svc # 检查服务状态,确认只运行在orcl1上 srvctl status service -db orcl -service aq_svc -v
第二步,如果需要精细调整负载均衡与切换属性,可以登录数据库用DBMS_SERVICE修改:
BEGIN
DBMS_SERVICE.MODIFY_SERVICE(
service_name => 'aq_svc',
clb_goal => DBMS_SERVICE.CLB_GOAL_LONG, -- 长连接,减少连接抖动
failover_method => DBMS_SERVICE.FAILOVER_METHOD_BASIC,
failover_type => DBMS_SERVICE.FAILOVER_TYPE_SESSION,
failover_retries => 10,
failover_delay => 5
);
END;
/第三步,客户端TNSNAMES中使用该Service名连接。注意这里的Service名即透明故障切换的入口,连接串不需要再写SID:
AQ_CONN =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = scan-ip)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = aq_svc)
)
)配置完成后,所有AQ生产者与消费者统一使用AQ_CONN连接。正常情况下它们都会被路由到orcl1;当orcl1故障时,Service自动切换到orcl2,队列消息因为存储在共享存储上,消费者重新连接后可以继续出队,实现业务层无感切换。
四、配置后的验证与常见误区排查
验证工作要从两个层面做。会话层面,让业务连接后查询gv$instance确认所在实例,多开几个连接看是否都落在orcl1:
-- 在业务会话中执行,确认当前实例 SELECT instance_name, host_name FROM v$instance; -- 在DBA侧统计各Service的会话分布 SELECT inst_id, service_name, COUNT(*) FROM gv$session WHERE service_name = 'aq_svc' GROUP BY inst_id, service_name;
性能层面,对比配置前后的等待事件。可以在AWR或gv$session_wait中观察gc buffer busy类等待是否明显下降,队列队表相关的enq: TX争用是否减少。如果吞吐量有量化数据,配置前后各压测一轮是最有说服力的验证方式。
常见的误区有三个。其一,只在TNS连接串里写了SERVICE_NAME但没在集群中用srvctl注册服务属性,结果SCAN监听按默认负载均衡把会话随机分配到两个节点,等于没配。其二,使用了旧的service_names参数方式(在init参数里直接设置),这种方式不会写入OCR,集群重启后Service不会自动在指定实例上拉起,RAC环境下应一律改用srvctl。其三,把AQ的Service配置成了双活(两个实例都是preferred),看似高可用,实则把队列热块争用又引回来了,除非使用Oracle 12c以后的Application Continuity配合队列分片方案,否则不建议这样配置。
总结来说,RAC环境下AQ与DML的稳定性高度依赖Service的实例绑定策略:专用Service绑定首选实例解决会话亲和性,TAF属性解决故障切换,srvctl注册保证配置随集群生命周期管理。把这三点做扎实,队列跨节点争用和DML全局缓存等待问题基本可以杜绝。
Oracle RACService配置AQ队列修改时间:2026-09-02 11:20:51