根本原因是tcp缓冲区满导致dg探测包卡在内核层,而非oracle心跳超时;需调大tcp_rmem/wmem max至16mb并配合sdu=32767,而非调整net_timeout。
主库压力大时心跳超时,根本原因不是心跳本身慢,而是网络协议栈缓冲区被填满、tcp重传加剧、ack延迟堆积,导致data guard进程(如lnsn)发出去的探测包在内核层就卡住,备库根本收不到——这和oracle层面的net_timeout设置无关,是底层传输通道已淤塞。
为什么默认TCP缓冲区在DG场景下容易成为瓶颈
Oracle Data Guard日志传输(尤其LGWR ASYNC模式)是持续高吞吐、小包+大包混合的流量:redo块切片后频繁发送SDU大小的数据包(默认8KB),而Linux内核默认的net.ipv4.tcp_rmem和net.ipv4.tcp_wmem三元组(如4096 16384 4194304)在突发写压下极易触顶。一旦发送队列积压,ss -i会显示retrans上升、qdisc排队增长,LNSn进程虽已调用send(),但数据卡在socket send buffer里,心跳探测自然“石沉大海”。
- 典型现象:
v$dataguard_stats中transport lag和apply lag同步跳涨,但log_archive_dest_status仍为VALID;netstat -s | grep "retransmitted"每秒递增 - 关键区别:这不是Oracle连接超时(如
EXPIRE_TIME),而是TCP连接“活着但哑了”——SYN/ACK正常,但应用层数据无法透出 - 影响范围:仅作用于DG专用链路;若共用监听器,需通过
tnsnames.ora中(SOURCE_ROUTE=yes)或独立服务名隔离
如何安全调大TCP接收/发送缓冲区
不能全局改/etc/sysctl.conf——会影响所有TCP连接,可能引发内存碎片或OOM Killer误杀。必须针对DG流量做流控级优化:
- 确认当前值:
sysctl net.ipv4.tcp_rmem net.ipv4.tcp_wmem,重点看第三个值(max)是否低于16MB - 临时生效(验证用):
sysctl -w net.ipv4.tcp_rmem="4096 262144 16777216",net.ipv4.tcp_wmem同理,将max设为16MB(16777216) - 永久生效(仅限DG网卡):在
/etc/sysctl.conf中追加net.core.wmem_max = 16777216和net.core.rmem_max = 16777216,再用ip rule+ip route为DG网段打标记,配合tc qdisc限速保底 - 必须同步调整Oracle:
tnsnames.ora中DG条目增加(SDU=32767),否则内核缓冲区再大,Oracle Net层仍按8KB切片,徒增系统调用次数
为什么NET_TIMEOUT调小反而让问题更糟
NET_TIMEOUT控制的是LNSn进程级重试间隔,不是TCP超时。当网络缓冲区已满,把NET_TIMEOUT从30秒改成5秒只会让LNSn更频繁地尝试send(),进一步加剧socket send queue堆积,触发更多TCP重传和拥塞控制退避,形成恶性循环。
- 错误操作示例:
ALTER SYSTEM SET LOG_ARCHIVE_DEST_2='SERVICE=standby LGWR ASYNC NET_TIMEOUT=5'; - 正确做法:先确保
tcp_wmemmax ≥ 16MB,再观察ss -i输出中send-q是否稳定在100KB以内;此时NET_TIMEOUT保持默认30秒即可 - 隐藏风险:某些云厂商虚拟网卡(如AWS ENA)对
tcp_rmemmax有硬限制,强行设为32MB会导致send()返回EAGAIN,必须结合ethtool -g调大ring buffer
真正要盯住的不是Oracle参数,而是ss -i输出里的retrans、send-q和rtt三项——它们不撒谎。一旦发现rtt波动超过2倍基线值,或retrans非零且持续增长,说明协议栈缓冲区已经成了瓶颈,这时候再调Oracle参数,只是给堵车路口多装几个红绿灯。











