运维管理8 分钟阅读
Oracle 慢 SQL 已经结束了,还能查到客户端 IP 吗?
2026年10月9日阅读—点赞—收藏—
OracleSQL
在知识库中专注阅读,并随时返回相关工具与课程
上一篇文章分享了一个排查 Oracle 慢 SQL 来源 IP 的方法。
当时接管一套 Oracle 11g 数据库,发现一条执行比较慢的 SQL,SQL_ID 是 fq5vvra6z6rt7。通过 V$SESSION 找到 SID,再关联 V$PROCESS 获取 SPID,最后在 Linux 上通过 netstat 查到了客户端 IP:10.245.40.123。
整个过程比较简单,几条命令就解决了。
不过这个方法有个前提:数据库会话还在,对应的网络连接也没有断开。
如果 SQL 已经执行结束,甚至会话都断开了,还能不能找到当时的客户端 IP?
另外,现在很多业务都是 Java 应用,通过连接池访问 Oracle。数据库看到的往往只是应用服务器 IP,如果想继续找到真正发起请求的客户端,又该怎么查?
最近专门翻了一下 Oracle 官方文档、Ask TOM 和网上的一些讨论,发现这两个问题还真没有想象中那么简单。
假设现在只有一个 SQL_ID,SQL 已经执行结束,V$SESSION 里找不到对应会话。
这种情况下,我一般会先想到 ASH。
Oracle 的 V$ACTIVE_SESSION_HISTORY 会对活跃会话进行采样,记录当时执行的 SQL_ID、SID、等待事件、客户端机器名等信息。
即使 SQL 已经执行结束,只要采样记录还在,就有机会找到它当时对应的会话。
1SET LINES 220 PAGES 10023SELECT TO_CHAR(sample_time,'YYYY-MM-DD HH24:MI:SS') sample_time,4 session_id,5 session_serial#,6 sql_id,7 machine,8 program,9 module,10 client_id11FROM v$active_session_history12WHERE sql_id = 'fq5vvra6z6rt7'13ORDER BY sample_time DESC;这里重点看 MACHINE、PROGRAM 和 CLIENT_ID。
假设查出来是这样的:
1SQL_ID MACHINE PROGRAM2------------- -------------- ----------------3fq5vvra6z6rt7 APP-SERVER-01 JDBC Thin Client至少说明这条 SQL 曾经在 APP-SERVER-01 对应的会话中执行过。
但问题也来了,MACHINE 通常是机器名,不一定是 IP。
网上有一种方法,是使用 UTL_INADDR.GET_HOST_ADDRESS 将机器名解析成 IP:
1SELECT DISTINCT2 machine,3 UTL_INADDR.GET_HOST_ADDRESS(machine) ip4FROM v$active_session_history5WHERE sql_id = 'fq5vvra6z6rt7';这个方法可以试,但不能完全依赖。
比如机器名无法解析,或者应用服务器有多个网卡,甚至 DNS 记录已经变更,最终查出来的 IP 就不一定是当时连接数据库使用的地址。
所以我更倾向于把 ASH 查到的 MACHINE 当成一个线索,再结合其他记录确认。
还有一点,ASH 是采样,不是把每条 SQL 都记录下来。如果 SQL 执行时间很短,可能根本没有被采样到。
查不到记录也很正常。
如果 SQL 是昨天甚至几天前执行的,内存中的 ASH 可能已经没有记录了。
这时候可以继续查 DBA_HIST_ACTIVE_SESS_HISTORY。
1SET LINES 220 PAGES 10023SELECT TO_CHAR(sample_time,'YYYY-MM-DD HH24:MI:SS') sample_time,4 instance_number,5 session_id,6 session_serial#,7 sql_id,8 machine,9 program,10 module,11 client_id12FROM dba_hist_active_sess_history13WHERE sql_id = 'fq5vvra6z6rt7'14 AND sample_time >= SYSDATE - 715ORDER BY sample_time DESC;相比内存 ASH,AWR 可以查询更早的历史记录。
不过要注意,AWR 保存的 ASH 数据也是采样记录,而且并不是把内存中的每一条 ASH 都完整保存下来。
因此,AWR 查不到,也不能说明这条 SQL 没执行过。
另外,同一个 SQL_ID 可能被多个应用服务器执行过。假设一条 SQL 每天执行几千次,直接按 SQL_ID 查询,可能返回很多不同的 SID 和 MACHINE。
这种情况下,最好先确认业务反馈的时间。
例如业务说昨天 14:30 左右出现卡顿,就把查询范围限制在 14:25 ~ 14:35:
1AND sample_time BETWEEN2 TO_DATE('2026-10-07 14:25:00','YYYY-MM-DD HH24:MI:SS')3AND TO_DATE('2026-10-07 14:35:00','YYYY-MM-DD HH24:MI:SS')再结合 SID、SERIAL#、实例编号,以及有记录时的 SQL_EXEC_ID、SQL_EXEC_START,缩小排查范围。
不过查到这里,通常还是机器名、程序名和会话信息,并没有直接拿到历史连接 IP。
如果确实需要确认当时的网络来源,就得继续往下找。
补充一句:ASH、AWR 涉及 Oracle Diagnostics Pack 授权,生产环境使用前需要确认授权情况。*
Oracle Listener 日志是另一个可以利用的地方。
正常情况下,客户端连接 Oracle 时,监听日志可能记录来源 IP、端口、服务名和连接时间。
先找到监听日志:
1lsnrctl statusOracle 11g 使用 ADR 时,日志通常在:
1$ORACLE_BASE/diag/tnslsnr/<hostname>/listener/trace/listener.log实际路径以监听配置为准。
可以先看看最近的连接记录:
1grep 'establish' listener.log | tail -20假设日志里有这样一条记录:
107-OCT-2026 14:30:12 *2(CONNECT_DATA=(SERVICE_NAME=repdb)) *3(ADDRESS=(PROTOCOL=tcp)(HOST=10.245.40.123)(PORT=45036)) *4establish * repdb * 0其中 HOST=10.245.40.123 就是监听收到连接请求时记录的来源 IP。
看起来问题解决了,但这里其实还有个坑:监听日志记录的是连接,不是 SQL。
假设应用服务器上午 9 点就通过连接池建立了 20 条数据库连接,下午 3 点业务执行了一条慢 SQL,那么监听日志记录的可能是上午 9 点的连接,而不是下午 3 点执行 SQL 的时间。
如果只拿 SQL 执行时间去匹配监听日志,很可能什么也查不到,而且同一个应用账号可能有几十甚至几百条连接,仅凭监听日志中的 IP,也不一定能确定是哪条连接执行了这条 SQL。
因此,监听日志更适合确认历史连接来源。如果要进一步对应到具体 SQL,还需要结合会话、连接时间、应用日志等信息。
如果监听日志已经清理,或者当时没有开启相关日志,那就没有办法从这里补查了。
除了监听日志,Oracle 11g 还可以看看 DBA_AUDIT_SESSION。
前提是数据库之前已经开启了相应的会话审计。
1SET LINES 220 PAGES 10023SELECT username,4 userhost,5 terminal,6 client_id,7 sessionid,8 TO_CHAR(timestamp,'YYYY-MM-DD HH24:MI:SS') logon_time,9 TO_CHAR(logoff_time,'YYYY-MM-DD HH24:MI:SS') logoff_time,10 returncode11FROM dba_audit_session12WHERE timestamp >= SYSDATE - 713ORDER BY timestamp DESC;这里可以查看数据库账号的登录时间、退出时间以及客户端主机信息。
但这个方法也有局限:
比如查到某个应用账号在 14:00 登录,不能直接认定它就是 14:30 执行慢 SQL 的那个会话,所以这个视图可以作为辅助证据,但不能只凭它就下结论。
前面几种方法,主要是在数据库侧找线索。
但如果应用通过连接池访问 Oracle,就会遇到另一个问题。
假设业务架构是这样的:
1用户浏览器2192.168.10.503 |4 v5Nginx / 网关610.10.10.107 |8 v9Java 应用服务器1010.245.40.12311 |12 v13Oracle 连接池14 |15 v16Oracle 数据库1710.245.40.113在数据库服务器上通过 netstat 查询,得到的 IP 大概率是 10.245.40.123,这本身没有查错。
因为数据库确实是和 Java 应用服务器建立的连接,浏览器并没有直接访问 Oracle,如果业务想知道究竟是哪个用户、哪台终端发起了请求,仅靠 Oracle 侧的会话信息通常就不够了。
需要继续查应用日志、网关访问日志,必要时还要结合 NAT 或代理设备的连接日志。这里最麻烦的其实不是 IP,而是如何把一次业务请求和一条数据库 SQL 对应起来。
比如同一台应用服务器上有几十个业务接口,所有接口都使用同一个数据库账号和连接池,Oracle 看到的可能都是:
1USERNAME : MES2MACHINE : APP-SERVER-013PROGRAM : JDBC Thin Client这种情况下,即使找到了应用服务器 IP,也不知道具体是哪个接口执行的 SQL。
关于这个问题,我在 Ask TOM 上找到过类似讨论。
当时有人问,WebLogic 使用连接池之后,数据库怎么知道具体是哪个应用用户执行了操作?
Oracle 给出的思路是,让应用在使用数据库连接时,主动设置会话标识。
例如:
1BEGIN2 DBMS_SESSION.SET_IDENTIFIER('user_10001');3END;4/设置后,在数据库里可以查询:
1SELECT sid,2 serial#,3 username,4 machine,5 program,6 client_identifier7FROM v$session8WHERE client_identifier = 'user_10001';这样至少能把数据库会话和应用业务用户关联起来。
如果还想知道具体是哪个接口,也可以使用 DBMS_APPLICATION_INFO 设置模块和操作名称:
1BEGIN2 DBMS_APPLICATION_INFO.SET_MODULE(3 module_name => 'MES-API',4 action_name => 'queryBatch'5 );67 DBMS_APPLICATION_INFO.SET_CLIENT_INFO(8 client_info => 'request=abc123'9 );10END;11/以后再排查 SQL,就不只是看到一个 JDBC Thin Client,而是有机会知道它来自哪个业务模块、哪个接口。
当然,应用侧还需要记录请求 ID、用户标识、终端来源等信息,才能继续向前追踪。
连接池还有个细节需要注意:一个数据库连接可能先后被多个业务请求使用。
因此应用每次获取连接时,都要设置当前请求的信息,使用完后还要清理,不能把上一个用户的标识留给下一个请求。
这件事最好在应用开发阶段就考虑进去。
等慢 SQL 已经发生、会话也断开了,再想办法补这些信息,通常就比较困难了。
前面查了一圈,我整理了一份简单的对照表,以后遇到类似问题可以直接参考。
这次查完,最大的感受是,Oracle 能查到多少,取决于当时留下了多少信息。
如果数据库前面还有连接池,那就不只是 DBA 自己能解决的问题了,需要应用配合,把业务用户、接口和请求信息传递到数据库会话里。
有些情况确实能追溯,有些情况只能找到应用服务器,还有些情况,历史信息已经丢失,根本无法准确还原。
这也是为什么我觉得,除了日常监控 SQL 性能,生产系统最好还能把 SQL 和具体业务请求关联起来。否则遇到慢 SQL,DBA 查到了执行计划,也找到了应用服务器,最后业务问一句“到底是谁触发的”,可能还是答不上来。