Oracle 网络层等待事件深度解析:SQL*Net message from/to client
在生产环境中,我们经常会看到 AWR/ASH 中高居榜首的两个事件:
SQL*Net message from client
SQL*Net message to client
SQL*Net message from client:到底在等什么?
官方定义
字段 | 说明 |
---|---|
事件名 | SQL*Net message from client |
本质 | 前台进程等待客户端发送下一条指令 |
P1 | 客户端网络驱动类型(Thin/OCI/JDBC/OCI over TCPS …) |
P2 | 期望接收的字节数 |
超时 | 无,直到客户端真的发消息 |
三种典型场景
- 真正的 Idle 一个交互式会话,用户正在屏幕前思考下一步操作。 ➜ 无需处理,不属于“性能问题”。
- 批处理“伪 Idle” PL/SQL 批处理程序把逻辑写在客户端(Java/Python),每取 1 行就计算一次,导致服务端 99% 时间都在等客户端。 ➜ 把计算搬到服务端(存储过程、BULK COLLECT + FORALL),或至少一次取
fetchSize = 1000
。 - 网络往返放大 应用框架默认
autoCommit=true
,每条 DML 后都有一次往返。 ➜ 设置useLocalTransaction=true
、批量提交。
如何量化
- AWR:
SQL*Net message from client
的 平均等待时长 高,但 单次等待 低 → 批处理循环。 - ASH:同一
sql_id
的等待分布,如果堆在from client
,说明 客户端代码 是瓶颈。
SQL*Net message to client:消息送不出去
字段 | 说明 |
---|---|
事件名 | SQL*Net message to client |
本质 | 服务端已准备好数据,但客户端或网络链路“吃不下” |
P1 | 客户端网络驱动类型 |
P2 | 本次发送的字节数 |
超时 | 无 |
常见根因
- 客户端 GC/CPU 飙高 JVM Full GC 时,应用线程无法读取 socket,服务端只能挂起。 ➜ 监控 JVM,调 GC 或加大
SDU/TDU
减少往返。 - 网络抖动/带宽不足 大量
LOB
/LONG RAW
导致 packet loss,TCP 重传。 ➜ 网络团队抓包;数据库侧启用SDU=65535
、TDU=65535
、TCP.NODELAY=YES
。 - 错误 JDBC 设置
defaultRowPrefetch=10
,结果集 100 万行时,服务端一次次“塞”数据。 ➜ 调大defaultRowPrefetch
(100~1000)或改用游标式流。
一条 SQL 引发的“血案”
现象
AWR 报告 SQL*Net message from client
占 DB Time 60%,单次仅 0.3 ms,次数 180 M。
定位
ASH 抽取 sql_id = 7r3m5...
对应 Java 方法 com.foo.Bar.processRow()
,发现伪代码:
while (rs.next()) {
total += rs.getBigDecimal("amount");
}
解决
在 Java 端把循环改为:
SELECT SUM(amount) INTO :1 FROM ...
一次往返搞定,事件直接消失。
行动清单(Checklist)
- AWR/ASH 先看 平均等待时长 vs 次数
- 若是批处理,优先
BULK COLLECT + FORALL
- 若是交互式,确认是否真 Idle
- 检查 JDBC 参数:
defaultRowPrefetch
、fetchSize
、autoCommit
- 网络层:
ping
,traceroute
,tcpdump
,oradebug tracefile_name
抓 SDU/TDU - 客户端资源:GC 日志、CPU、内存
下次再看到这两个事件,别再一股脑把锅甩给网络——多数时候,真正的瓶颈在应用和数据库之间的 交互模式。