尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Oracle连接超时排查指南:从ORA-12170到sqlnet.ora参数全解析

Oracle连接超时排查指南:从ORA-12170到sqlnet.ora参数全解析 有个场景我印象特别深朋友的应用连接池里经常报ORA-12170: TNS: Connect timeout occurred他第一反应是数据库负载高让我帮忙抓 AWR结果实例闲得发慌后来又怀疑监听器挂了lsnrctl status一看又是 READY。折腾半天最后我在客户端sqlnet.ora里加了SQLNET.OUTBOUND_CONNECT_TIMEOUT15现象才从“卡 120 秒后报错”变成“15 秒快速失败”。虽然根因在中间网络设备上但这个参数至少让应用有了快速重试的机会。这个经历让我意识到Oracle 的连接超时并不是单一参数而是一整套参数在连接过程的不同阶段各自设防。这篇文章更像是我学习 Oracle 各类连接超时相关参数时整理的笔记。我会把一条连接从客户端到数据库服务器所经过的每一段分别对应到具体的超时参数再附上实际踩过和见过的案例。适合正在被 ORA-12170、ORA-12535、ORA-3136 这类错误折磨的 DBA也适合应用侧想调连接池参数的开发。1. 一条 Oracle 连接的完整旅程每个环节都可能超时很多人以为“连接超时”就等于“数据库连不上”但在排查时必须先搞清楚到底是哪一段没有完成。一个普通的 Oracle 远程连接从应用发起到底层数据库会话建立至少要经历这几个步骤应用通过 JDBC 驱动或 SQL*Plus 发起连接请求。解析 tnsnames.ora拿到目标主机 IP、端口和服务名。发起 TCP 连接完成三次握手。客户端发送 Oracle Net 层的连接数据包。监听器listener收到请求校验客户端是否有权限接入。监听器为这个连接派生出专用服务器进程Dedicated Server Process。客户端与服务端进程完成身份认证。会话正式建立可以执行 SQL。后续每次 SQL 执行都要进行数据包收发的往返。这里的每一步都可能成为“超时”的源头第 3 步目标主机不可达、防火墙丢包或网络拥塞时TCP 层会按系统默认策略反复重试客户端可能要等 75 秒甚至更久才放弃。这个阶段主要由SQLNET.OUTBOUND_CONNECT_TIMEOUT限制。第 5 到第 8 步客户端虽然已经到达监听器但认证流程没有在规定时间内完成。这个阶段由服务端的SQLNET.INBOUND_CONNECT_TIMEOUT或监听器参数INBOUND_CONNECT_TIMEOUT_listener接管。第 9 步连接建立之后如果网络出现闪断、对端僵死普通 TCP 协议很难快速感知。需要靠SQLNET.SEND_TIMEOUT、SQLNET.RECV_TIMEOUT这类参数兜底。会话建立起来之后如果应用一直占着会话不释放或者连接池里的连接变成半开状态数据库侧的 profile 资源限制IDLE_TIME、CONNECT_TIME会强制清场。所以排查超时问题的第一件事就是先回答“我卡在哪个阶段”。把这个搞清楚比盲目调大任何一个参数都重要。调错了方向比如问题出在认证阶段却去调客户端出站超时那只是在错误的位置往火上浇油。2. sqlnet.ora 里的四个关键参数位置不同作用阶段完全不同sqlnet.ora是 Oracle Net 的核心配置文件它既可以放在客户端也可以放在服务器端。大家最常听到的超时参数基本都集中在里面但四个参数分别管的是连接建立前、连接建立中和建立后的读写。2.1 SQLNET.OUTBOUND_CONNECT_TIMEOUT客户端发起连接的“止损线”这个参数配置在发起连接那一侧的sqlnet.ora文件中。如果应用服务器通过 JDBC 连数据库那么它应该配置在应用服务器上的$ORACLE_HOME/network/admin/sqlnet.ora或者$TNS_ADMIN指定的目录里。它控制的是从客户端开始建立 TCP 连接起到整个 Oracle Net 层连接完成所需的最大时间。也就是说它覆盖的是刚才链路里的 TCP 三次握手、发送连接请求、等待监听器响应这几个动作的总和。如果超过这个时间还没完成客户端直接放弃报ORA-12170。需要注意Oracle 11.2 之后这个参数的默认值是 60 秒。很多环境里不显式配置但它一直是生效的。问题在于 60 秒对一个在线业务来说太久了——如果网络或者防火墙真的有问题应用线程会在这个时间段内全部被占住连接池迅速打满业务表现为大面积卡顿。所以我一般会根据实际网络延迟把它调整到 15 到 30 秒# 客户端 sqlnet.ora SQLNET.OUTBOUND_CONNECT_TIMEOUT 30这里有个细节容易被忽略这个参数不只是应用服务器连数据库时生效。数据库服务器本身也可能作为客户端去连接另一个数据库比如通过 dblink所以如果 dblink 经常超时也要在源端数据库的sqlnet.ora里看这个值。2.2 SQLNET.INBOUND_CONNECT_TIMEOUT服务端拒绝慢连接的阀门这个参数是服务端的通常配置在数据库服务器自己的sqlnet.ora里。它限制的是监听器从收到 TCP 连接开始到客户端完成认证、监听器把连接交给 server process 为止的时间。默认也是 60 秒。它的作用是保护监听器如果没有这个限制客户端可以建立 TCP 连接后一直不发数据或者认证过程极慢监听器的进程和资源会被这类半连接占满。设置之后超过时间还没完成认证的连接会被服务端主动断开。此时数据库的告警日志里会出现WARNING: inbound connection timed out (ORA-3136)客户端那边可能看到ORA-12535: TNS: operation timed out也可能直接是连接被断开。实际使用中这个参数不建议随意调得很大。如果服务端频繁出现 ORA-3136优先排查的是“为什么认证这么慢”而不是直接改成 300 秒。常见原因包括 DNS 反向解析慢、认证服务比如 LDAP响应慢、服务器 CPU 严重超卖等。当然如果确实有合法的重负载场景导致认证超过 60 秒把它调到 90 或 120 秒也不是不行但必须同时观察监听器的负载情况。2.3 SQLNET.SEND_TIMEOUT 与 SQLNET.RECV_TIMEOUT连接建立后的读写超时这两个参数控制的是连接建立之后一次 send 或 recv 操作的最长等待时间。默认值是 0也就是禁用。它们可以配置在客户端也可以配置在服务端官方更建议两端配合使用。打个比方OUTBOUND_CONNECT_TIMEOUT和INBOUND_CONNECT_TIMEOUT管的是“门卫验票”阶段而SEND_TIMEOUT和RECV_TIMEOUT管的是“入场后半天不见人”的情况。网络闪断、交换机异常、应用服务器掉电等场景TCP 连接不会立刻断开服务端进程会一直傻等客户端的数据。如果不开类似的检测机制这种僵尸会话会一直挂在数据库上直到 TCP 层最终超时或被人肉清理。启用方式如下# sqlnet.ora 同时配置在客户端和服务端 SQLNET.SEND_TIMEOUT 60 SQLNET.RECV_TIMEOUT 60不过我个人在生产环境里会非常谨慎地使用这两个参数。如果设置得太小遇到一次正常的网络抖动正在执行的 SQL 可能直接被中断业务会误以为数据库出了问题。我通常会在网络质量比较差、且频繁出现僵死会话的环境里才启用值一般也是 60 秒起步。2.4 SQLNET.EXPIRE_TIME不是超时参数但经常和超时一起提SQLNET.EXPIRE_TIME虽然不叫 timeout但和连接超时关系密切。它配置在服务端表示每隔多少分钟发送一个探测包检查空闲连接是否还活着。默认 0 表示禁用。它和 TCP keepalive 的作用类似但发生在 Oracle Net 层能够感知到数据库端的连接是否真的可用。一个常见场景是应用服务器和数据库之间有防火墙或 NAT 设备连接空闲超过一定时间后中间设备会把连接静默清掉但两端都不知道。配置了SQLNET.EXPIRE_TIME之后Oracle 会定期发包让中间设备保持连接映射同时也能发现已经死掉的连接。我一般至少会把它设置为 10 分钟# 服务端 sqlnet.ora SQLNET.EXPIRE_TIME 103. listener.ora 和用户 profile容易被忽略的另外两层配置s qlnet.ora之后很多人就以为超时参数学完了实际上监听器层和数据库会话层还有两个“藏得比较深”的超时相关配置。前者影响连接能否建立后者影响会话能活多久。3.1 监听器层的 INBOUND_CONNECT_TIMEOUT 与 CONNECT_TIMEOUT在listener.ora里有一个INBOUND_CONNECT_TIMEOUT_listener_name参数比如默认监听器名字是 LISTENER就叫INBOUND_CONNECT_TIMEOUT_LISTENER。它的作用和sqlnet.ora里的SQLNET.INBOUND_CONNECT_TIMEOUT一样但优先级更高如果listener.ora里配置了它就用listener.ora的值没配才看sqlnet.ora两边都没有才是默认 60 秒。另外还有一个比较老的CONNECT_TIMEOUT_listener_name同样在listener.ora里早期版本用得比较多。如果同时配置了INBOUND_CONNECT_TIMEOUT_listener和CONNECT_TIMEOUT_listener前者优先。查看当前实际生效的值不需要去翻文件直接lsnrctl show inbound_connect_timeout lsnrctl show connect_timeout我遇到过有人在listener.ora里把INBOUND_CONNECT_TIMEOUT_LISTENER写成了 5 秒结果应用一启动就大量报连接建立失败。原因很简单应用连接池在高峰期会同时建立很多连接服务端认证稍微慢一点点就超过了 5 秒阈值。这种属于“参数设得太激进”导致的超时和网络问题无关。所以每次调整监听器层超时都要用lsnrctl reload让配置生效并回去看listener.log里的错误时间点确认是超时还是其他原因。3.2 profile 里的 IDLE_TIME、CONNECT_TIME、IDLE_BLOCK_TIME数据库用户 profile 里也有几个和超时相关的资源限制它们属于会话级策略主要用来对付“连接建立成功后长期不干活”的会话。IDLE_TIME会话连续空闲多少分钟后被强制断开单位是分钟。CONNECT_TIME一个会话从建立到结束总共能存活多少分钟到点就断单位是分钟。IDLE_BLOCK_TIME会话处于阻塞状态比如正在等锁时允许空闲多少分钟到点就断。最常见的坑是DBA 辛辛苦苦建了 profile设置了IDLE_TIME但数据库参数RESOURCE_LIMIT还是默认的 FALSE导致这些限制完全不生效。在 Oracle 里profile 里的资源限制要真正起作用必须先把RESOURCE_LIMIT打开ALTER SYSTEM SET RESOURCE_LIMITTRUE SCOPEBOTH;确认当前是否生效SHOW PARAMETER RESOURCE_LIMIT;设置示例CREATE PROFILE APP_USER_PROFILE LIMIT IDLE_TIME 30 CONNECT_TIME 480;然后把 profile 分配给用户ALTER USER APP_USER PROFILE APP_USER_PROFILE;用下面这条 SQL 可以看到 profile 里的限制是否已经生效SELECT resource_name, limit FROM dba_profiles WHERE profile APP_USER_PROFILE AND resource_name IN (IDLE_TIME, CONNECT_TIME, IDLE_BLOCK_TIME);如果应用连接池里的连接长时间处于空闲状态数据库侧把连接断开是很正常的操作。但应用侧如果没做重连或校验机制就会拿到一个已经失效的连接表现就是“连接偶发中断”。所以设置 profile 超时最好和应用连接池的空闲回收策略一起规划。3.3 三层超时参数的叠加关系我把这三层整理成一张表方便排查时对号入座阶段对应位置参数默认情况典型报错客户端发起连接客户端 sqlnet.oraSQLNET.OUTBOUND_CONNECT_TIMEOUT60 秒11.2 起ORA-12170监听器接收与认证服务器 sqlnet.ora / listener.oraSQLNET.INBOUND_CONNECT_TIMEOUT / INBOUND_CONNECT_TIMEOUT_60 秒ORA-12535 / ORA-3136连接建立后读写两端 sqlnet.oraSQLNET.SEND_TIMEOUT / SQLNET.RECV_TIMEOUT禁用连接中断 / ORA-12637会话空闲与总时长数据库 profileIDLE_TIME / CONNECT_TIMEUNLIMITEDORA-02396死连接探测服务端 sqlnet.oraSQLNET.EXPIRE_TIME禁用无明显报错4. 案例复盘一应用频繁报 ORA-12170我按什么顺序查这个案例来自一套生产系统应用服务器和数据库服务器在一个机房的不同网段中间隔了防火墙。应用日志里每天固定几个时间段会出现 ORA-12170和数据库监控里的负载曲线对不上。我的排查顺序是这样的。先看监听器状态确认 listener 有没有挂lsnrctl status显示 READY没问题。又确认正在监听的端口lsnrctl services也没发现异常。接着用tnsping从应用服务器侧测连接字符串tnsping ORCL大多数时候能通偶尔会跳一次高延迟。需要强调tnsping只验证监听器是否可达它不会完成数据库身份认证所以tnsping通不代表整个连接链路没问题。然后去看 listener.log。位置一般在$ORACLE_HOME/network/log/listener.log我用 grep 把错误时间段里的记录捞出来grep 12:0[0-9].*ERROR listener.log发现报错集中在两边同时有大量连接建立的时刻每次超时前都有 TCP 连接出现但很快被重置的痕迹。到这里基本判断是网络层的问题但还需要进一步坐实。接下来在应用服务器上启用客户端 trace。在客户端sqlnet.ora里加TRACE_LEVEL_CLIENTSUPPORT TRACE_FILE_CLIENTclient TRACE_DIRECTORY_CLIENT/tmp/oracle_trace重现一次报错后打开 trace 文件可以看到连接在“wait for connect response”阶段持续的时间非常长已经超过 60 秒。这就确定是 TCP 建立或 Oracle Net 连接包发送环节出了问题而不是数据库内部认证慢。同时用 tcpdump 抓包tcpdump -i eth0 -nn host 192.168.10.100 and port 1521抓到的包里有大量 TCP SYN 重传而且重传间隔越来越长说明 SYN 包发出去之后服务端没有及时回 ACK或者防火墙设备在特定流量特征下丢包。最后和网络团队确认是对端防火墙上有针对该源 IP 段的每秒新建连接数限制高峰期超过阈值就把后续 SYN 丢掉客户端只能反复重试最终在 60 秒后报 ORA-12170。这个案例的最终修复是网络团队调整了防火墙策略。但从数据库侧能做的是在客户端sqlnet.ora里把这个超时设得更短让应用快速失败、快速切换而不是全部线程卡死在等待上SQLNET.OUTBOUND_CONNECT_TIMEOUT 15调整后应用的报错虽然还会偶发出现但业务感知从“线程池被占满、大面积超时”变成“单次连接快速失败、连接池自动重试”整体影响大大降低。这也是我反复强调的超时参数不只是用来“等更久”更多时候是用来“更快地失败”。5. 案例复盘二ORA-3136 的根因居然是 DNS 反向解析另一个案例比较典型数据库的 alert log 频繁出现WARNING: inbound connection timed out (ORA-3136)应用侧偶尔报 ORA-12535但频率不高而且报错时段数据库负载很低网络 ping 值也正常。按照 ORA-3136 的提示这是服务端入站连接超时说明客户端已经连上了监听器但没能在限定时间内完成后续步骤。我先看了监听器日志发现超时发生的阶段不是在 TCP 建立时而是在客户端已经发出连接请求之后。这个细节很关键它让我把排查重点从网络连通性转到了认证和握手流程上。由于连接池建立的连接本身很多我怀疑是认证环节被拖慢。先检查用户密码认证方式SELECT username, authentication_type FROM dba_users WHERE username APP_USER;确认不是外部认证后又把注意力放到数据库服务器上。用抓包工具观察监听器端口上的流量发现一个现象连接建立后会有很长一段时间几乎没有业务数据包进出而在这段“空白期”里服务器端好像在等待什么。继续排查发现服务器在处理到达的 TCP 连接时会对客户端 IP 做反向 DNS 解析。这个反向解析的响应时间非常不稳定慢的时候超过 60 秒导致整个入站连接超时。用下面的命令直接验证反向解析延迟time nslookup 192.168.20.55果然有时候几秒有时候几十秒。这就解释了为什么数据库负载不高、网络也不丢包却会出现 ORA-3136。最终解决方法是网络组修正了内部 DNS 的反向解析记录同时在数据库服务器的/etc/hosts里加上应用服务器 IP 和主机名的映射避免每次连接都去查 DNS。之后 alert log 里的 ORA-3136 基本消失。这个案例说明ORA-3136 并不一定意味着“连接太多”也可能是认证链路或系统调用层面的延迟。如果盲目把SQLNET.INBOUND_CONNECT_TIMEOUT调大虽然能掩盖问题但每次认证仍然要浪费几十秒一旦连接量上来还是会出事。顺便提一下另一个容易混淆的错误ORA-28547: connection to server failed, probable Oracle Net admin error。这个错误字面上也有 connection failed但它通常是 Oracle Net 层配置错误比如客户端和服务器端版本不兼容或者本机装了多个 Oracle Client 导致加载了错误的 Oracle Net 配置。它和超时参数没有直接关系排查时要先检查客户端的sqlnet.ora、tnsnames.ora以及环境变量是否指到了正确的ORACLE_HOME别一上来就调超时参数。6. 应用侧超时参数与数据库服务端参数如何配合很多时候连接超时不是数据库侧参数单独造成的而是应用连接池、JDBC 驱动和数据库参数三者没有对齐。下面聊几个实际操作中会用到的配置点。6.1 JDBC Thin 驱动的两个关键属性Oracle 的 JDBC Thin 驱动支持通过连接属性设置超时最常见的是oracle.net.CONNECT_TIMEOUT建立 TCP 连接的超时时间单位毫秒。oracle.jdbc.ReadTimeoutsocket 读取超时时间单位毫秒。在代码里这样设置Properties props new Properties(); props.setProperty(user, scott); props.setProperty(password, tiger); props.setProperty(oracle.net.CONNECT_TIMEOUT, 10000); props.setProperty(oracle.jdbc.ReadTimeout, 60000); Connection conn DriverManager.getConnection( jdbc:oracle:thin://dbserver:1521/ORCL, props);oracle.jdbc.ReadTimeout这个参数要特别小心。它的本质是 socket 的SO_TIMEOUT也就是说如果数据库执行一条大查询并且在指定时间内没有向客户端发送任何网络数据客户端就可能抛出 SocketTimeoutException。对于复杂的报表查询、批量任务这个值不能设得太小否则会把真正在跑的大任务误杀。我一般在 OLTP 系统里会设 60 秒以上在跑批系统里干脆不设或者在任务级别单独管理。6.2 连接池侧怎么配HikariCP 是 Java 生态里比较常见的连接池它和 Oracle 配合时的几个超时参数值得认真看dataSource: className: oracle.jdbc.pool.OracleDataSource maximumPoolSize: 20 connectionTimeout: 30000 validationTimeout: 5000 idleTimeout: 600000 maxLifetime: 1800000connectionTimeout调用方从连接池获取一个连接的最大等待时间默认 30 秒。validationTimeout连接被借出前或归还后做一次连接有效性检查的最大等待时间。idleTimeout连接池中空闲连接的最大存活时间。maxLifetime连接在池中的最长生命周期。Oracle 官方连接池 UCP 也有类似概念比如setConnectionWaitTimeout控制获取连接的最大等待时间setConnectionValidationTimeout控制验证超时。核心思路是一样的。这里有个配合关系需要想清楚应用从连接池拿连接时会先尝试新建 TCP 连接到数据库。如果数据库端的SQLNET.OUTBOUND_CONNECT_TIMEOUT设得比连接池的connectionTimeout还大那么网络异常时连接池会先因为等待时间超时而报错。这个时候应用看到的错误就不是 ORA-12170而是“获取连接超时”排查的人容易忽略底层网络问题。所以我的习惯是让客户端的SQLNET.OUTBOUND_CONNECT_TIMEOUT略小于等于应用连接池的connectionTimeout这样网络故障时底层连接先快速失败连接池再把这个失败包装成数据库连接错误方向就清晰多了。6.3 服务端、客户端、连接池统一规划的参考值我整理了一份相对稳妥的起点配置具体环境还要根据实际网络延迟和业务场景微调层级参数建议起始值客户端 sqlnet.oraSQLNET.OUTBOUND_CONNECT_TIMEOUT15-30 秒服务端 sqlnet.oraSQLNET.INBOUND_CONNECT_TIMEOUT60 秒异常时再微调服务端 sqlnet.oraSQLNET.EXPIRE_TIME10 分钟服务端 sqlnet.oraSQLNET.RECV_TIMEOUT / SEND_TIMEOUT默认禁用必要时 60 秒数据库 profileIDLE_TIME30 分钟连接池connectionTimeout15-30 秒连接池validationTimeout5 秒JDBCoracle.net.CONNECT_TIMEOUT与连接池获取连接超时匹配JDBCoracle.jdbc.ReadTimeoutOLTP 建议 60 秒以上这套值不是放之四海而皆准的但它能帮你把问题控制在相对可控的范围。我自己在实际操作里的体会是先确定故障发生在哪一段再动参数。每改一个参数都要留着变更记录然后回到 listener 日志和 alert log 去验证。连接超时这类问题最怕的是一口气同时调五六个参数改完也不知道是哪一个起了作用下次换一个环境又要从头踩一遍。
返回列表