ORA-12170排查实录:分清两个inbound timeout参数 做数据库运维这些年ORA-12170我见得不算少。它全称是TNS:Connect timeout occurred字面意思就是连接超时发生了。最近一次比较典型的案例是这样的一套跑在Oracle 19c上的业务系统每天晚上固定有一个批量任务会重建连接池时间大概持续三五分钟。批量任务启动时应用日志里就开始刷ORA-12170有时候一刷就是上百条。奇怪的是同样的时间段我手动在服务器上sqlplus连接数据库完全正常应用服务器上单独连也正常唯独应用连接池批量并发创建连接时大面积失败。这类手动正常、批量失败的故障最迷惑人。一开始按常规思路排查网络让网络团队检查链路来回测了好几轮结果都是抓包正常、没有丢包。后来我直接在数据库服务器上抓了一次1521端口的包才注意到一个被忽略的细节TCP三次握手全部完成之后服务器端几乎在同一时间主动把连接断开时间非常规律离TCP建立差不多是固定秒数。这个固定秒数成了突破口——它对应的就是listener.ora和sqlnet.ora里的两个超时参数INBOUND_CONNECT_TIMEOUT_监听器名 和 SQLNET.INBOUND_CONNECT_TIMEOUT。这篇文章就把这两个参数的原理、配置和排错完整梳理一遍。1. 一次ORA-12170排查两个timeout参数把我绕晕了1.1 故障现场应用池批量报错手动连接却正常像前面说的情况最让人头疼的就是手动测不出问题。手动sqlplus连接时一次就一个连接监听器瞬时就能处理完从建立TCP连接到完成认证可能只需要几十毫秒。但应用连接池冷启动或扩容时会在短时间内发出几十上百个connect请求监听器处理速度跟不上请求涌入速度后面的连接在中间环节一等就是好几秒。如果这时候网络延迟稍微高一点单个连接在某个阶段停留的时间很容易超过参数阈值于是被系统主动断开。被断开后客户端那头的TCP栈要再过一阵子才能感知最后抛给应用的就是ORA-12170。这类问题大多不是网络真断了而是连接建立速度跟不上超时参数过紧共同作用的结果。手动测正常不代表生产正常尤其连接池场景必须按高并发批量建连的条件来复现否则很多超时问题永远只在业务高峰期出现。1.2 数据说话listener日志和alert日志里的超时痕迹顺藤摸瓜看了数据库的alert log在里面发现了这样的警告记录WARNING: inbound connection timed out (ORA-3136)同时listener.log里也出现了一部分类似下面的记录TNS-12535: TNS:operation timed out TNS-00505: Operation timed out把时间戳跟应用报错的时间一对完全吻合。这个时候我已经基本确定问题不是网络而是服务器端有超时机制在主动掐连接。接下来的问题是到底是哪一个超时机制因为这套环境里listener.ora写了INBOUND_CONNECT_TIMEOUT_LISTENER 10sqlnet.ora也写了SQLNET.INBOUND_CONNECT_TIMEOUT 60两个参数都有到底哪个先触发、为什么高峰期触发这就是我花了不少时间的地方。实际上如果只有listener.ora里的参数解决起来还简单一些恰恰是两个参数同时存在名字又长得太像导致排查时一度把时间耗在确认它们到底是什么关系上面。这次之后我把两个参数完整梳理了一遍发现不少同事对它们的理解都存在误区尤其是把SQLNET.INBOUND_CONNECT_TIMEOUT当成监听器参数的别名来用其实这是完全不同的两个东西。1.3 为什么手动连接没问题批量连接就超时超时参数最核心的机制是阶段停留时间。一条连接从TCP建立到最后认证完成中间有几个等待点每个等待点都可能成为超时的计时窗口。批量建连时几十上百个请求同时涌到监听器内核accept队列一下子就堆满了。监听器进程只能一个个处理排在后面的连接即使TCP已经握手完成也要在队列里等着被accept然后继续等CONNECT DATA、等版本协商、等认证反馈每一段等待都在消耗超时预算。手动连接几乎没有排队所以无论参数设成多少都从容批量连接则把超时预算用得干干净净。理解了这一点再去设置两个参数脑子里就不是抄个数字而是每个阶段到底能容忍连接等多久。2. 两个参数的分工边界监听器管门口Oracle Net管认证通道2.1 一次成功的数据库连接实际要过三道门客户端到Oracle数据库表面上就是一个连上了但内部其实分成好几个阶段。我习惯把过程拆成三段第一段是TCP连接建立。客户端发起SYN数据库服务器回复SYNACK客户端再回ACK三次握手完成后TCP连接进入监听器的accept队列。第二段是监听器接收并解析连接请求。监听器把TCP连接从内核accept上来之后开始等待客户端发送CONNECT DATA数据包。这个包里面包含了服务名、连接方式等关键信息类似于你进小区时跟门卫说我要去几栋几单元。监听器根据这些信息判断目标服务是否存在、是否允许连接然后把连接交给后续的server process。第三段是会话建立和认证。监听器把连接转交之后server process开始和客户端进行版本协商、参数协商接着验证用户名密码完成这些之后才真正建立会话。如果客户端在这个阶段停住不动连接就会一直挂在这里。这三个阶段对应了两种超时机制这就是名字相似但作用完全不同的根本原因。2.2 两个timeout覆盖的时间窗拿一张表看最清楚把两个参数的对照关系整理成了一张表工作中我经常拿这张表跟同事对答案它应该能回答90%的这俩到底啥区别疑问。对比维度INBOUND_CONNECT_TIMEOUT_监听器名SQLNET.INBOUND_CONNECT_TIMEOUT所在配置文件listener.orasqlnet.ora服务器端归属进程监听器进程Oracle Net 服务端进程计时起点监听器接受TCP连接后等待客户端发送CONNECT DATA连接进入Oracle Net、开始认证等后续交互之前计时终点收到完整CONNECT DATA之前客户端完成认证、会话建立之前默认值未显式设置时11.2以后通常继承SQLNET.INBOUND_CONNECT_TIMEOUT的值60秒11.2起引入触发后动作监听器直接终止连接服务器进程终止连接客户端常见报错ORA-12170、ORA-12525ORA-12170服务器端痕迹listener.log里出现TNS-12535/TNS-00505alert log里出现ORA-3136警告这两个参数在时间上是串联关系客户端必须先通过监听器的检查才会进入后续Oracle Net阶段的认证如果第一道门就被拦下来第二个参数根本不会触发。反过来即使第一道门顺利通过如果连接在认证阶段卡住第二个参数会兜底把连接杀掉。2.3 为什么需要两道关卡而不是一个超时管到底有人可能觉得一个超时参数不是更简单吗为什么要弄两个答案是它们防护的阶段和风险不一样。监听器层的入站连接超时主要防的是僵尸连接。有些客户端建立了TCP连接但迟迟不发送CONNECT DATA也不断开。如果监听器不做限制这种半截连接会一直挂在accept队列里占用监听器的处理能力。大量这类慢连接堆在一起监听器会逐渐失去响应正常用户反而连不上。这就是典型的慢速连接拖垮服务的场景。Oracle Net层的入站连接超时针对的是另一种风险连接已经通过了监听器但客户端在后续的认证环节一直不动作。这个阶段连接已经被转交给server process了一个悬挂的连接等于浪费一个进程资源。在高并发场景下如果一批连接都在认证阶段挂着数据库的process数量很快会被占满影响范围比监听器层更大。两道关卡各自守一个阶段恰好把资源风险最大的两个窗口都覆盖这才是两个参数存在的真正意义。理解了这个逻辑配置的时候你就知道不是随便抄一个值就行而是要分别考虑两个阶段的业务特征。2.4 版本时间线为什么有些老库没有SQLNET这个参数这里要特别提醒SQLNET.INBOUND_CONNECT_TIMEOUT不是一直都有的它是Oracle 11.2以后新增的参数。10g、11.1这样比较老的版本配置超时只能靠listener.ora里的INBOUND_CONNECT_TIMEOUT_监听器名没有第二个参数可用。如果手上还有老版本库网上搜索资料时要注意区分凡是把SQLNET.INBOUND_CONNECT_TIMEOUT写进sqlnet.ora的配置在10g上多半不生效甚至会因为不认识的参数导致sqlnet.ora解析异常。这也是一个常见的版本坑。做跨版本升级时建议把两个参数的默认行为和版本支持情况一并检查别把11.2的配置惯性直接搬回老环境。3. 配置文件里的正确写法和最容易踩的三个坑3.1 标准配置示例先给一份标准写法。listener.ora中INBOUND_CONNECT_TIMEOUT_后面的名字要跟监听器名保持一致比如监听器叫LISTENER参数就是INBOUND_CONNECT_TIMEOUT_LISTENER监听器叫PROD_LISTENER参数就是INBOUND_CONNECT_TIMEOUT_PROD_LISTENER。listener.ora示例LISTENER (DESCRIPTION_LIST (DESCRIPTION (ADDRESS (PROTOCOL TCP)(HOST db01)(PORT 1521)) ) ) INBOUND_CONNECT_TIMEOUT_LISTENER 10sqlnet.ora示例SQLNET.INBOUND_CONNECT_TIMEOUT 60注意sqlnet.ora写的是数据库服务器端不是客户端。很多应用服务器上也有sqlnet.ora文件但SQLNET.INBOUND_CONNECT_TIMEOUT只有数据库服务器上的那份才生效客户端的设置不会起任何作用。listener.ora里那个参数的位置非常关键它必须放在监听器定义的括号外面也就是顶层。INBOUND_CONNECT_TIMEOUT_LISTENER不是一个地址描述属性它是监听器级别的全局配置位置摆错了配置可能不会报错但行为完全不符合预期。3.2 坑一参数写进了LISTENER块内部lsnrctl reload还不报错这是我见过最多的低级错误。有人把参数写成了这样LISTENER (DESCRIPTION_LIST (DESCRIPTION (ADDRESS (PROTOCOL TCP)(HOST db01)(PORT 1521)) (INBOUND_CONNECT_TIMEOUT_LISTENER 10) ) )从语法上看多了一个属性lsnrctl reload通常不会报错但INBOUND_CONNECT_TIMEOUT_LISTENER会被当成DESCRIPTION里的一个无用属性完全不参与监听器的超时控制。检查时用lsnrctl show inbound_connect_timeout看一眼如果显示的值跟你预期不一致多半就是写错位置了。我的建议是写完listener.ora之后手动lsnrctl reload然后立刻用lsnrctl show inbound_connect_timeout确认当前生效值别想当然觉得reload没报错就是成功了。顺带也检查一下sqlnet.ora里的SQLNET.INBOUND_CONNECT_TIMEOUT确认两个值都是你想要的再往下做业务验证。3.3 坑二改了参数没生效其实是集群环境在作祟非集群环境listener.ora一般放在$ORACLE_HOME/network/admin下面修改后lsnrctl reload就能热加载。但碰到RAC或Restart环境监听器往往由集群软件管理listener.ora的实际路径可能是$GRID_HOME/network/admin或者监听器配置已经纳入了集群资源管理。这种情况下直接手动改listener.ora下次集群重启监听器时配置可能被覆盖或回退。我之前排查过一个集群环境就碰到过改了INBOUND_CONNECT_TIMEOUT_LISTENERreload之后生效了但过了几天集群做了资源重定位配置又变回旧值。安全做法是先确认监听器到底由谁托管ps -ef | grep tnslsnr看看监听器的进程路径和父进程或者用集群命令查看监听器配置。由集群托管的监听器优先考虑通过集群管理命令修改而不是直接改文件。还需要提醒的是补丁升级、集群维护过程也可能重置监听器配置生产环境升级前后都该检查一遍这个参数是否还是你想要的。3.4 坑三SQLNET.INBOUND_CONNECT_TIMEOUT配置到了客户端白忙一场第三个坑更隐蔽。有人会在客户端sqlnet.ora里也写上SQLNET.INBOUND_CONNECT_TIMEOUT以为客户端可以主导连接超时结果故障仍然出现。因为这个参数是服务端参数它作用于数据库服务端进程和监听器客户端根本管不着。客户端真正能控制的类似参数是SQLNET.OUTBOUND_CONNECT_TIMEOUT控制客户端到服务器建立连接的超时两者方向不同作用位置也不同。这个坑最容易出现在网上找配置直接抄的场景里。抄的时候根本不看文件在哪个机器上结果sqlnet.ora被抄到了应用服务器上问题依旧还浪费一整天的排查时间。所以配置前第一件事确认你在改的sqlnet.ora是不是数据库服务器上的那份。4. 超时值怎么定默认值、经验值和不同场景的调整方向4.1 默认值、继承关系和版本差异先明确几个基线。SQLNET.INBOUND_CONNECT_TIMEOUT从11.2开始默认值是60秒12c、19c、21c上依然是这个值。INBOUND_CONNECT_TIMEOUT_监听器名的默认行为稍微绕一点在11.2以后的版本如果listener.ora里没有显式配置监听器会继承sqlnet.ora中SQLNET.INBOUND_CONNECT_TIMEOUT的值。我在测试环境验证过把listener.ora里的INBOUND_CONNECT_TIMEOUT_LISTENER删掉只保留sqlnet.ora里的60秒然后用telnet裸连1521端口挂住不发CONNECT DATA大概60秒后TCP连接被服务器主动断开——说明监听器确实把继承来的值用上了。老版本11.1及以前这个参数默认是0也就是不做限制。所以默认有没有限制这个问题答案跟版本有关。排查问题时如果发现连参数都没配但连接会被掐断先看看版本和继承关系不要觉得一定是防火墙干的。4.2 按业务场景给一个参考值区间取值这件事网上说法很杂有人喜欢3秒、5秒有人喜欢120秒。我给一个基于实际场景的参考适合大多数常规业务但最终要以本地的连接建立速度和峰值并发量为准。业务类型INBOUND_CONNECT_TIMEOUT_监听器名SQLNET.INBOUND_CONNECT_TIMEOUT备注普通OLTP连接池稳定10~1560默认常规推荐高峰期连接池频繁重建/扩容20~3060~90防止排队误杀跨机房/跨地域广域网30~6090~120网络RTT高时需要放宽高安全要求的内网环境5~1030需配合网络质量评估这里特别强调一点超时值不是越小越安全。设得太小高峰期正常的连接排队也会被误杀业务报警比慢速攻击更早到来。设得偏大确实给慢速连接留了更多时间但你要想清楚真正决定安全的是整体设计包括网络ACL、防火墙、资源限制而不是一个超时值。比较稳妥的做法是先把值设在10-15秒的区间跑一段时间结合下面的信号判断是否需要调整。4.3 判断当前值过紧的三个信号我一般看三个信号。第一listener.log里TNS-12535或TNS-00505的出现频率。偶尔几条可能是异常如果每个业务高峰都稳定出现说明监听器层超时太紧。第二alert log里ORA-3136警告的数量。alert log出现inbound connection timed out持续增加对应的是SQLNET.INBOUND_CONNECT_TIMEOUT这条线。第三客户端报错的时间规律。应用侧如果每次报ORA-12170都集中在批量连接窗口而且从发起连接到报错的时间间隔几乎一致比如总是10秒左右这就是被对应timeout触发的最直观特征。出现这些信号后把对应参数调大50%到100%再观察一个高峰周期看错误是否消失或大幅减少。如果消失说明调整方向是对的然后再逐步收敛到合适值。4.4 不要把设为0当作万能解法讨论超时参数时常有人问设成0是不是就彻底关掉超时一劳永逸确实某些版本中设为0表示不限制。但从安全角度看我强烈不建议在生产环境把这两个参数设成0除非有明确理由并且配套了其他防护手段。根本原因是把入站连接超时关掉等于把慢速连接拖死监听器/占满进程的风险重新放回到系统里。安全加固不是把所有限制都拿掉而是让限制更精准。如果真的有特殊场景需要放松可以先用较大的值比如120秒、180秒过渡观察确认没有副作用再评估是否进一步收紧而不是直接一刀切设0。5. 带着ORA-12170走一遍定位流程判断是哪个参数拦了连接5.1 第一步把两份日志拉出来对照遇到ORA-12170我第一步从来不是动配置而是拉日志。数据库服务器上的alert log和listener.log都要看。alert log搜索关键词是ORA-3136或inbound connection timed outlistener.log搜索关键词是TNS-12535、TNS-00505。把命中条目的时间戳和应用报错时间放到一起比对基本能确认是不是超时参数干的以及触发点是哪一道门。如果alert log里有大量的ORA-3136同时listener.log相对干净说明连接通常闯过了监听器这一关是认证/协商阶段的SQLNET.INBOUND_CONNECT_TIMEOUT在拦截。反过来如果listener.log里一堆TNS-12535而alert log几乎没动静那就是连接在发送CONNECT DATA之前就被监听器层断了INBOUND_CONNECT_TIMEOUT_监听器名是主要嫌疑。部分极端情况下两个日志都有记录说明两个阶段都有连接卡住。5.2 第二步利用固定秒数反推是哪个参数超时参数最大的特点就是准。被超时机制断开的连接从建立到断开的时间间隔会精确逼近配置值。所以查看应用日志里的报错时间如果从连接发起算起每次都差不多是某个固定秒数比如10秒、60秒直接对比当前配置的值就能初步判断。这里有个小技巧本机到数据库机房如果网络往返很稳定这个固定秒数会非常接近参数值误差通常在几百毫秒以内如果跨地域网络要考虑往返延迟。比如配置里INBOUND_CONNECT_TIMEOUT_LISTENER10而客户端实测从连接发起到报错间隔约10.5秒几乎可以断定是这个参数被触发。5.3 第三步抓包确认连接卡在哪个阶段日志反推足够快但真要实锤还是抓包。在数据库服务器上用tcpdump抓1521端口tcpdump -i any -nn -s0 host 客户端IP and port 1521 -w /tmp/connect_timeout.pcap然后复现一次应用报错打开抓包文件看时间线。关注三个东西TCP三次握手的SYN/SYN-ACK/ACK是否完成、之后客户端有没有发出CONNECT DATA包TNS协议数据payload里能看到服务名、服务器端断开连接的包是FIN还是RST。如果三次握手完成但没有CONNECT DATA说明客户端在发数据前就停滞了停在监听器层的timeout窗口里。如果CONNECT DATA已经发出、客户端也收到了来自服务器的响应但后续认证包迟迟不来停下来的是认证阶段归SQLNET.INBOUND_CONNECT_TIMEOUT管。这两种情况干预的方向完全不同抓包能帮你少走很多弯路。5.4 第四步临时调大参数做一次验证定位已经基本明确之后我习惯在业务低谷做一个动态验证把嫌疑最大的参数临时调大比如从10秒调到60秒观察一个高峰周期。注意是只调一个参数不是两个一起调否则你无法知道真正起作用的是谁。如果调大后故障消失说明判断正确。这时候不要急着庆祝要回头分析为什么客户端的连接建立会慢到触发超时——是应用连接池的配置问题网络往返确实太慢还是DNS解析在拖后腿把根因挖出来比把参数调大更有价值。调大参数只是缓解了表面报错如果高峰期观察到的排队延迟已经超过阈值太多调大只是让系统更容忍排队但治标不治本。另外动态调参记得留好回滚方式改之前把原始值记下来测试完成后要么恢复要么按新评估值做正式变更。临时改完忘记恢复下次出问题就把自己带进坑里了。5.5 DNS和防火墙这些替罪羊怎么排除聊到这里可能有人问那跟网络相关的超时怎么区分我的经验是网络问题首先会有随机性。防火墙丢包、网络拥塞导致的连接超时报错间隔通常不固定忽长忽短而超时参数触发的连接被断秒数几乎恒定这是两者最直观的区别。另外防火墙问题往往伴随客户端的TCP重传抓包能看到大量SYN或SYN重传超时参数触发则是握手完成后被动断开。DNS解析问题则是另一类常见替罪羊如果数据库服务器上监听器配置使用了主机名且服务器DNS反向查询超时连接建立也会被拖慢最终被超时机制掐掉——这种问题和参数大小无关需要单独解决DNS解析路径。6. 可以直接照抄的生产配置参数模板6.1 参数模板最后给一份我目前在常规生产环境用的参数模板。这个模板适合大多数OLTP系统不算激进稳定性优先。sqlnet.ora数据库服务器上SQLNET.INBOUND_CONNECT_TIMEOUT 60listener.ora数据库服务器上顶层LISTENER (DESCRIPTION_LIST (DESCRIPTION (ADDRESS (PROTOCOL TCP)(HOST db01)(PORT 1521)) ) ) INBOUND_CONNECT_TIMEOUT_LISTENER 15如果监听器名不是LISTENER记得把参数名后缀改成实际监听器名。这个模板的逻辑是监听器层给15秒覆盖TCP握手后到CONNECT DATA递交的窗口Oracle Net层给默认60秒覆盖认证及协商阶段作为兜底。两个值都不算紧能容忍一定程度的网络波动和连接排队又不至于放任半截连接长期占资源。对于连接池特别大、每天有冷启动重建的系统我建议先把INBOUND_CONNECT_TIMEOUT_LISTENER临时放大到30秒跑几天统计一下高峰期的排队耗时再决定要不要永久放宽。我见过太多系统一上来就照着最小化抄了个5秒结果第二天高峰期就开始大量超时最后又临时调回去。生产环境的参数调整节奏比数值更重要。6.2 验证配置生效的实操方法配置改完用下面这套动作验证基本不会漏。首先监听器参数修改后执行lsnrctl reload然后执行lsnrctl show inbound_connect_timeout确认显示的秒数和listener.ora一致。如果显示的不是预期值多半是写错位置或监听器名不匹配。其次用telnet做个裸连测试模拟只建立TCP连接、不发数据的僵尸连接telnet dbhost 1521连接建立后什么都不输入观察连接会不会在预期秒数后被断开。如果在15秒左右被断开说明监听器层的入站超时生效了如果一直挂着不动说明这个监听器当前的inbound timeout没有生效或者被显式设成了不限。测试前确认当前没有其他安全软件干扰并且这个端口允许这种测试。最后复测一次正常的应用连接确认业务没有受影响。整个变更最好留下一行记录写明改前值、改后值、生效方式、观察结果。倒不是说一定要做多严格的流程而是这种名字相近的两个参数最容易在半年后被自己记混文字记录能省很多事。6.3 半年下来我自己沉淀的判断步骤最后分享一点个人习惯。现在我遇到任何连接类问题无论是ORA-12170、ORA-12525还是连接挂起都会强制自己先过一遍这个清单当前版本是几sqlnet.ora和listener.ora分别在哪个机器、哪个路径两个超时参数配置的值是多少listener.log和alert log里有没有对应的超时记录客户端报错的时间是否规律这套下来大多数连接超时问题都能在十分钟内定位到参数层面。剩下的少数情况才值得上抓包工具去跟TCP层、DNS层较劲。认识这两个参数不需要背文档只要把进门和入座两个窗口想清楚再结合日志验证就足够应对生产环境里绝大部分坑了。