问题现象
工作中碰到了开发的应用系统,不定期出现访问不了的情况。具体表现为,当其它应用调用它的接口时,可能会出现超时,过一段时间后,再调用它的接口可能又正常了。为了排除此接口是否和连接数据库有关,使用Python的Flask框架demo,只返回字符串。经过数小时后访问,并没有超时。。。。
这一步,排除了用户端和应用层的网络连接是正常的。
我们重新定义问题原因:
应用没有办法及时地查询到数据库,应用返回数据时间过长,导致调用该应用接口超时。这种情况比较偶发,一天大概几次,没什么特别规律。
接着,我们在应用程序中增加了连接数据库连接,使用简易的sql语句查询,第一次访问正常。
(为使得每次在触发数据库连接时,异常能被抛出来,增加了主动断开会话)
于是第二天,再次curl请求接口,在后台观察接口,没给出响应,随后大约有30秒,接口抛出异常了,如图

ORA-03113/ORA-03114这类错误大致就是丢失了数据库连接,客户端向Oracle服务器提交SQL时,由于TCP连接已经中断,这时客户端侦测到连接中断,那么客户端就会报ORA-03113/ORA-03114这类错误,然后会话中断。
暂时可理解为:应用端与数据库建立的tcp连接因为某些原因断开了,而导致了“管道破裂”。一般数据库连接池会与数据库保持长连接,在需要的时候省去建立连接的过程,直接使用,但是,提出疑问:
1、为什么这些空闲的连接会被断开呢?
2、被谁断开了?
3、数据库连接池不会自主维护连接吗?
连接为什么会被断开
服务环境
百思不能其解,比如从应用服务器到数据库的连接太多?明显不是,这个项目还在试运行阶段,用的人不多。
真实环境的应用程序使用Python-Flask的微框架开发,在Linxu服务器上部署,同时采用了WSGI HTTP服务器(Gunicorn)管理,gunicorn有个检测应用状态机制,并可设置超时(默认30秒),如果被管理的服务超时,将会重启进程程序,我们的超时现象也归属于gunicorn管理,查看应用日志,gunicorn使得超时的应用程序大量的重启,也没有太多其它的信息:
现在已经确定是应用失去连接,导致连接不上数据库的故障,但,为什么该应用会连接不上数据库并且还是偶发性的呢?
关于这个问题,和负责此系统的同事排查了好久。。,竟然没有任何头绪,快验收了,这个问题也没解决着实给客户不好交待,于是,决定抱着学习的心态,攻破它!而我,也没太多经验可谈,而最近又各种杂活巨多,只能抽空班后的时间,才能集中精力去排查。再说这个系统是我测试的,不管哪里的原因,出了问题。脸上也没光,好吧。其实是因为这个应用当初是我部署上去的,不能允许自己部署的应用出现问题。然而,事实证明,这个问题涉及到的水比较深。
不涉及太多的网络链路,部署架构如图,划分为应用服务器、数据库服务器,服务器部署及应用服务的


可能原因的解释
中间我们先捋捋思路及步骤,先从应用区来分析,再分析数据库区,接着就是他们之间的网络问题,
出现连接不上数据库的那个应用是Python的Flask程序,flask是个微服务开发框架,本身没有内置orm框架,需要依赖第三方模块,它使用了Flask框架的扩展库SQLAlchemy连接池来连接数据库。而数据库本身是Oracle数据库,
认识 Flask-SQLAlchemy
出现连接不上数据库的那个应用是Python的Flask程序,flask是个微服务开发框架,本身没有内置orm框架,需要依赖第三方模块,
而这里介绍的Flask-SQLAlchemy是一个为您的 Flask 应用增加 SQLAlchemy 支持的扩展。本质上是对sqlalchemy的进一步封装;换句话说:在Flask中,我们使用了Flask-SQLAlchemy来操作数据库,
当一个线程请求调用Flask-SQLAlchemy,SQLAlchemy 会创建一个数据库连接,执行结束后把连接放回池子里。下一个请求来的时候,就可以直接使用之前的连接。当然,如果同时进来多个不够分配的时候,会创建另外的连接用于使用,执行结束后又放回池子里。池子里的最大连接数是可以配置的。这种方式可以避免频繁创建、销毁连接,从而提高执行效率。
SQLAlchemy 是提供了 recyle 的机制来处理连接过久的 connection,如果一个 connection 占用时间太长超过了 pool_recyle 设置的时间,这时候获取新连接的时候,pool 会自动失效该连接,
我们先不验证这个问题,这个问题交给连接池的工作机制去处理。总是我们程序调用连接池需要有效的连接。
Flask-SQLAlchemy配置
配置选项 说明
配置键
Flask-SQLAlchemy 扩展能够识别的配置键的清单:
SQLALCHEMY_DATABASE_URI
用于连接数据的数据库。例如:
sqlite:////tmp/test.db
mysql://username:password@server/db
SQLALCHEMY_BINDS 一个映射绑定 (bind) 键到 SQLAlchemy 连接 URIs 的字典。 更多的信息请参阅 绑定多个数据库。
SQLALCHEMY_ECHO 如果设置成 True,SQLAlchemy 将会记录所有 发到标准输出(stderr)的语句,这对调试很有帮助。
SQLALCHEMY_RECORD_QUERIES 可以用于显式地禁用或者启用查询记录。查询记录 在调试或者测试模式下自动启用。更多信息请参阅 get_debug_queries()。
SQLALCHEMY_NATIVE_UNICODE 可以用于显式地禁用支持原生的 unicode。这是 某些数据库适配器必须的(像在 Ubuntu 某些版本上的 PostgreSQL),当使用不合适的指定无编码的数据库 默认值时。
SQLALCHEMY_POOL_SIZE 数据库连接池的大小。默认是数据库引擎的默认值 (通常是 5)。
SQLALCHEMY_POOL_TIMEOUT 指定数据库连接池的超时时间。默认是 10。
SQLALCHEMY_POOL_RECYCLE 自动回收连接的秒数。这对 MySQL 是必须的,默认 情况下 MySQL 会自动移除闲置 8 小时或者以上的连接。 需要注意地是如果使用 MySQL 的话, Flask-SQLAlchemy 会自动地设置这个值为 2 小时。
SQLALCHEMY_MAX_OVERFLOW 控制在连接池达到最大值后可以创建的连接数。当这些额外的 连接回收到连接池后将会被断开和抛弃。
SQLALCHEMY_TRACK_MODIFICATIONS 如果设置成 True (默认情况),Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。这需要额外的内存, 如果不必要的可以禁用它。
应用程序端数据库连接池
但是,这里带来另一个问题。当数据库突然挂掉或者数据库过一定时间清理未活动连接的时候,SQLAlchemy 是不知道的。当一个请求进来时,被分配的连接,实际上对于数据库服务器来说,是失效的连接
当数据库异常故障
连接池的连接在不知情的情况下在数据库服务器上被关掉了。这个大致分为两种情况:
一、数据库异常或者误操作,比如数据库挂掉、重启、有的连接被误操作给 kill 掉了等 等这种情况下只好重启应用程序,让应用重新维护连接池;
二、连接长时间未活动。有两种情况:
- 连接池中的连接的回收时间大于数据库配置的未活动连接过期时间,由于连接的回收时间一般都是设置的两个小时,而数据库(mysql)的未活动连接过期时间默认是八个小时,所以这种情况一般不会出现;
- 应用程序里的逻辑代码有问题,在进行数据库的写入操作后,缺少 commit、 rollback、close 的操作将连接放回连接池,连接池没法管理,当这个连接被数据库回收后(或者网络干预),也就出现了上面的异常。但是这种情况不会出现,SQLAlchemy已经帮我们完成,如果真的出现,有以下几个解决办法:
(1)查看相关的业务,补充缺失的连接维护操作(推荐);
(2)可以通过重启应用程序解决,不过指标不治本,运行一段时间后,问题依旧会出现;
(3)SQLAlchemy 开启 autocommit,不过这样就不能手动 rollback 了,在很多插入、更新场景中,不大实用;
(4)彻底一点的,直接不用连接池,在 create_engine 时指定参数 poolclass 为 NullPool。不过连接的使用效率就不如之前了.
TCP连接
既然是应用程序连接不上数据库,不会是tcp连接的原因吧,那么可以观察下网络连接。
要查网络,首先想到,linux系统命令“netstat” ,即:netstat -antpo | grep 1521。(另外可以通过tcpdump去抓包,然后打开wireshark去分析,在应用刚启动和应用空闲时(即应用不主动去访问数据库),虽然应用和数据库之间已正常建立了连接,在应用和数据库服务器两端均可看到已经建立连接的tcp的“ESTABLISHED”状态,但抓包却发现它们之间没有传递任何数据包,后面补充当时抓包详情)
由于应用采用数据库连接池来连接Oracle数据库,也就是说,应用与数据库之间的连接是tcp长连接。那么正常情况下,在任一时刻,应用与数据库服务器两端的ESTABLISHED状态的tcp连接数量应该都是一致的。然而,我实际观察到的情况,却不是这样。(此处应该有截图,当时没保存下来)
经过更加细致的观察,可以发现,一开始,在该应用刚刚启动时,两端的tcp连接数量是相同的。但是,过了大约2个小时后,oracle数据库服务器端与应用服务器的tcp连接数量急剧减少。也就是开文的现象。
这里有个疑问:
两端的ESTABLISHED状态的tcp连接数量不一致是不是一定就有问题的呢?答案是肯定的。因为tcp是面向连接的协议,连接的两端只有通过"三次握手"正常建立了tcp连接后,它们才能正式通过该tcp连接开始交互数据。如果其中一端不存在该tcp连接的信息,那么即便另一端通过该tcp连接发送带应用数据的数据包过来了,不存在tcp连接信息的这一端收到数据包后也会直接将该数据包丢弃,从而导致两端无法正常交互数据。因此,问题就出在这了,正是因为应用和数据库服务器两端的tcp连接不对称,才导致了应用无法正常访问数据库。简单来说,我们使用netstat命令即可查询出结果来。
这说明,因为某种原因,从应用服务器发出的数据包在网络中丢失了。具体是什么原因,现在还无法确定。
按理说,数据包在网络中丢失了,那么,就应该找网络工程师一起排查下是什么问题。但无奈,线上环境的对接人,他们可能不是专业的网络工程师,得到的回复是,没有看到防火墙关于连接时间的额外设置。简单来说,我从那里没有获取到任何有价值的信息,他们也不认为网络本身有什么问题,也不愿意协助排查,我也没有权限看不了防火墙,所以我只能自己想办法了。
我在抓包时,我发现的怪异的现象。其中,第一个现象是,在应用刚启动,应用空闲时(即应用不主动去访问数据库),虽然应用和数据库之间可以正常建立连接,在应用和数据库服务器两端均可看到等量的ESTABLISHED状态的连接,但抓包却发现它们之间没有传递任何数据包。
关于这个问题,我查阅了<<TCP/IP >>。其中,TCP Keepalive章节里面有提到:
Many newcomers to TCP/IP are surprised to learn that no data whatsoever flows across an idle TCP connection. That is, if neither process at the ends of a TCP connection is sending data to the other, nothing is exchanged between the two TCP endpoints. There is no polling, for example, as you might find with other networking protocols. This means that we can start a client process that establishes a TCP connection with a server and walk away for hours, days, weeks, or months, and the connection should remain up. In theory, intermediate routers can crash and reboot, data lines may go down and back up, but as long as neither host at the ends of the connection reboots (or changes its IP address), the connection remains established. This is how TCP/IP was designed.
上面这段话的意思是:空闲的TCP连接是没有数据流动的,也就是说,如果TCP连接的两端如果都不发送数据给对方,就没有任何东西在它们之间交互;也不会有定时的数据包轮询;当一个客户端和一台服务器建立TCP连接之后,若服务器离开几个小时、几天或几个月,连接也都还在的,即便中间的路由器和线路挂了、重启了或断了,但只要TCP连接两端的主机都没有重启(或更改它的IP地址),连接会一直保持ESTABLISHED状态;TCP/IP就是这么设计的。
也就是说,我观察到的第一个现象(应用空闲时,虽然应用服务器和数据库服务器之间建立了ESTABLISHED状态的连接,但却没有数据包交互),该现象是正常的。TCP/IP就是这么设计的。
而我们又发现如下规律:
当经过2个小时后,观察两台机器的tcp连接状态;
1、数据库服务器的tcp连接“ESTABLISHED”消失。
2、应用服务器的tcp连接“ESTABLISHED”存在。
看到这里我们似乎找到了问题的根源,为什么数据库所在的连接会丢失呢?是不是中间有防火墙,防火墙是否会设置一个TCP超时时间,是否设置的就是2小时呢,防火墙这时候在拆除连接时,不会向连接的两端发送任何数据来通知连接已经拆除?,这样的话,调大防火墙的超时时间一切问题就解决了啊!!
一堆疑问,我第二天立马联系了机房管理员,说没有查看到关于防火墙连接额外的设置(可能不是专业的吧,再说不能因为你一个系统,让他们更改防火墙规则。)。。无奈只能检查自己的问题;
TCP保持连接
通过查资料,找到TCP 存在一种TCP keepalive机制,但它是可选的。TCP keepalive特性并不是TCP规范的一部分(因为可能会有误判及产生额外的流量等),然而,大多数TCP实现都提供了keepalive能力。即便如此,TCP keepalive功能默认也可能是关闭的。
TCP keepalive功能可以在TCP连接的其中一端,或两端启用,或两端都不启用。它的运作机制是这样的:如果TCP连接上有一段时间(叫做keepalive time)没有活动,那么,启用了keepalive的一端就会发送存活探测数据包给它的对端。如果没有收到任何响应数据包,该探测包就会周期性地(周期间隔为keepalive interval)重复发送,直到探测次数达到keepalive probes的设置值。当发生这种情况时,对端的系统将被判定为是不可恢复的并且该TCP连接会被终止。
变量keepalive time、keepalive interval和keepalive probes的值通常是可以修改的。
一些系统允许在per-connection的基础上进行这些修改,另一些系统则只能在全局范围修改(或可能两种都支持)。在Linux中,这些值可以通过sysctl系统参数来相应修改:
net.ipv4.tcp_keepalive_time、
net.ipv4.tcp_keepalive_intvl
net.ipv4.tcp_keepalive_probes。
默认值分别是7200 (seconds,或2 hours)、75 (seconds)和9 (probes)。
Linux并未提供一个原生的工具来为那些未请求启用TCP keepalive的应用强制开启TCP keepalive功能。换句话说,虽然Linux支持TCP keepalive特性,也可以调整相关的定时器设置,但某一个TCP连接究竟是否启用TCP keepalive特性,由上层的应用程序说了算(需要应用程序调用系统的API),Linux本身并没有工具/设置项来让你为所有TCP连接都强制开启TCP keepalive特性。
说到这里我们会提出疑问,应用层的数据库模块在开发时,应用程序应该有这样的连接会话机制?
不妨试想,Python应用数据库模块在开发时让设置tcp keepalive为true(控制开关的作用),这样tcp连接在一定时间内没有任何数据报文传输则启动探测,但是这个时间一般是操作系统规定,Linux系统中可以通过设置net.ipv4.tcp_keepalive_time来修改,默认是7200秒,即2小时。
当然在编程时也可以设置这个时间用于当前socket,而Java或者Python的Socket API中好像只有设置keepalive=true,并没法设置tcp_keepalive_time,当设置了tcp keepalive之后,只要tcp探测包发送的时间小于防火墙的连接超时时间,防火墙就会检查到连接中仍然有数据传输,就不会断开这个连接。
经过查找相关资料,使用Python的SQLALCHARY创建的连接是没有开启keepalive的,你怎么知道的呢?
这点可以通过Linux的netstat或ss命令在数据库客户端(即应用端)验证,使用命令netstat -ano 或 ss -ano,其中参数o都是显示timer计时器,timer计时器在连接建立状态下可以对连接保活计时,netstat命令对没有开启keepalive的tcp连接显示为:off (0.00/0/0),ss命令对没有keepalive的tcp连接,不会显示timer计时器

那么有人提出疑问了,程序中人为的也可以主动做这种心跳检测啊,经查阅,Java使用的druid连接池可配置属性testOnBorrow=true,默认为false)但我们的Flask-SQLAlchemy并没有提供打开keepalive的开关,
所以我们只能手动打开tcp的keepalive了,上面我们了解了tcp的keepalived的机制后,所以只要两端服务器的一端开启,就可以保持连接的不会空闲,而应用端可能服务比较多,机器数也多,我们打开数据库所在服务器即可。
难道Oracle没有tcp keepalive的机制?
查阅资料后,发现:Oracle提供了类似tcp keepalive的机制,也就是DCD(Dead Conneciton Detection)。在$ORACLE_HOME/network/admin/sqlnet.ora文件中增加如下一行:
sqlnet.expire_time=NNN
这里NNN为分钟数,Oracle数据库会在会话IDLE时间超过这个指定的时间时,检测这个会话的对端(即客户端)是否还有效。避免客户端由于异常退出,导致会话一直存在。同样的如果DCD的时间比防火墙切断空闲连接的时间短,连接也可以一直保持,
我尝试性的设置了2,就是2min时间,这个时间间隔绝对小于防火墙的超时机制了,下来,修改后重新加载监听配置,使其生效(不是重启),过了1小时后,再去访问接口,问题缺还是依然存在(待确定,是否有tcp的keepalive开关,或者设置路径不对)
于是手动前去修改操作系统内核,回到问题本身,服务器是centos系统,属于Linux的一种,自然也支持上面提到的三个属性:
[root@bx ~]# sysctl -a | grep 'keepalive'
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_intvl = 75
各参数含义:
net.ipv4.tcp_keepalive_time:如果TCP连接启用了TCP keepalive特性,那么,当TCP连接空闲时间达到该项设定值时,服务器就会发送存活探测数据包给它的对端。默认值是7200 (seconds,或2 hours)。
net.ipv4.tcp_keepalive_probes:发送存活探测数据包后,若未收到来自对端的响应数据包,服务器会尝试重新发送探测数据包,直到总的探测次数达到该项设定值。默认值是9 (probes)次。若最后一次探测时仍未收到对端的响应数据包,服务器会清除掉自己本端的该TCP连接。
net.ipv4.tcp_keepalive_intvl:每次发送探测数据包的间隔时间。默认值是75 (seconds)。那么来说,从开始探测到最后清除TCP连接,总共需要经过tcp_keepalive_intvl * tcp_keepalive_probes的时间,若以默认值计算,为75 * 9 = 675秒,约为11分钟多。
即便如此,我又怎么知道应用服务器去往数据库服务器的TCP连接是否启用了TCP keepalive呢?毕竟,我无法知道应用程序是否有要求启用TCP keepalive。其实,通过netstat命令的 -o 选项可以查看到,在应用服务器上执行以下命令:
[bx@bxsoft-m ~]$ netstat -ontp|grep 1521
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 10.10.135.72:55902 10.10.138.32:1521 ESTABLISHED 18404/python3 off (0.00/0/0)
tcp 0 0 10.10.135.72:48400 10.10.138.32:1521 ESTABLISHED 13561/python3 off (0.00/0/0)
tcp 0 0 10.10.135.72:57820 10.10.138.32:1521 ESTABLISHED 9368/python3 off (0.00/0/0)
tcp 0 0 10.10.135.72:49872 10.10.138.32:1521 ESTABLISHED 15731/python3 off (0.00/0/0)
tcp 0 0 10.10.135.72:55904 10.10.138.32:1521 ESTABLISHED 18386/python3 off (0.00/0/0)
tcp 0 0 10.10.135.72:57092 10.10.138.32:1521 ESTABLISHED 15069/python3 off (0.00/0/0)
tcp 0 0 10.10.135.72:49104 10.10.138.32:1521 ESTABLISHED 29833/python3 off (0.00/0/0)
tcp 0 0 10.10.135.72:55906 10.10.138.32:1521 ESTABLISHED 18313/python3
[bx@bxsoft-m ~]$
最后一列都是off的,说明在应用服务器这端,所有与数据库服务器的TCP连接都是没有启用TCP keepalive特性的。如果有启用的话,那么最后一列会显示keepalive的字样。
我们修改数据库所在服务器的内核,再次查看
[root@bxsoft-db ~]# netstat -ontp|grep 1521
tcp 0 0 10.10.138.32:36331 10.10.138.32:1521 ESTABLISHED 6938/ora_lreg_orcl off (0.00/0/0)
tcp6 0 0 10.10.138.32:1521 10.10.135.73:34876 ESTABLISHED 32412/oracleorcl keepalive (17.41/0/0)
tcp6 0 0 10.10.138.32:1521 10.10.135.73:42854 ESTABLISHED 4487/oracleorcl keepalive (55.29/0/0)
tcp6 0 0 10.10.138.32:1521 10.10.135.72:49872 ESTABLISHED 32231/oracleorcl keepalive (69.63/0/0)
tcp6 0 0 10.10.138.32:1521 10.10.135.72:55886 ESTABLISHED 10852/oracleorcl keepalive (35.84/0/0)
tcp6 0 0 10.10.138.32:1521 10.10.135.72:57090 ESTABLISHED 27031/oracleorcl keepalive (11.26/0/0)
tcp6 0 0 10.10.138.32:1521 10.10.135.72:57088 ESTABLISHED 26902/oracleorcl keepalive (11.26/0/0)
tcp6 0 0 10.10.138.32:1521 10.10.135.72:49104 ESTABLISHED 3762/oracleorcl keepalive (9.73/0/0)
tcp6 0 0 10.10.138.32:1521 10.10.135.73:34384 ESTABLISHED 27302/oracleorcl
改完后!执行生效命令,准备过1小时后,再去请求接口,进行验证,
[bx@bxsoft-b ~]$ curl http://10.10.135.73:8004/hc
<h2>I am tcc_login, I am fine.</h2>
<h2>Current db has 338 tables.</h2>
<h2>Current db in oracle://c##xjcx:xjcx@10.10.138.32:1521/orcl.</h2>
<h2>It takes 0.051158905029296875 seconds.</h2>
<h2>Current app ip: 10.10.135.73 </h2>
<h3>系统访问人数统计:</h3>
<table border="1"><tr><th>周期</th><th>访问人数</th></tr><tr><td>2020</td><td>338</td></tr><tr><td>total</td><td>338</td></tr></table>
[bx@bxsoft-b ~]$
立即登陆系统(早上的时间去访问接口,之前测试必会出现超时),接口是再正常不过的访问速度了,我发了朋友圈,本系统的项目经理评论说:是xxx系统问题吗?,我说:“是”,他再次回复了我一个大拇指。(仅仅只想记录下自己认真的经历);虽然这个系统在公司项目的分量不大,但是这一刻的感觉真不错!这个问题得以解决!
问题总结
到这里为止,问题已经很清楚了。这种问题的发生其实并不局限于访问Oracle数据库的情况,只要是长连接,当满足以下所有条件时,这种问题就会发生:
1、 两台服务器之间存在有硬件防火墙。
2、 使用了TCP长连接,比如在使用TCP连接池的场景中。
3、 在TCP连接的两端均未启用TCP keeplive特性,或任意一端或两端启用了TCP keeplive特性,但空闲检测时间(如Linux的net.ipv4.tcp_keepalive_time参数,AIX的tcp_keepidle参数)大于硬件防火墙的会话超时时间。是否有启用TCP keeplive特性通常由上层应用程序决定,但可以通过命令来观察。
4、 不存在应用层的定时轮询,或存在应用层的定时轮询,但轮询间隔大于硬件防火墙的会话超时时间。是否存在应用层的定时轮询通常要根据具体的应用程序及配置(如数据库连接池的配置)而定。
5、 在硬件防火墙的会话超时时间内,长连接中没有数据包传输。通常,如果应用本身没什么访问量,或者是在凌晨等时段访问量比较低,又或者是长连接数量太多,都有可能使某些长连接一直空闲,长时间没有数据包传输。
其它问题
有时,我们会想,客户端的数据库连接池中有很多与数据库服务器端的连接,假设其中仍然有一部分连接是仍然可以访问到数据库的,那么这时候应用访问数据库会不会有问题呢?
仍然是可能有问题的。当应用访问数据库时,应用可能是从连接池中随机选取一个连接的(具体的选取规则我不是很确定),但重点是,在没有做检测的情况下,应用并不知道连接池中的哪些连接是实际可用的。
如果应用选取到的是一个实际不可用的连接,那么这时,由于数据包发送出去了但没有回包,(至少在Linux上)操作系统会一直尝试重传并超过最大重试次数(正如前面所说),这个过程至少也要10分钟以上,而应用层通常早就超时报错了。
当确定了连接不可用之后,应用会再从连接池中选取一个连接。若这个连接仍然不可用,应用才会尝试创建一个新连接。整个过程下来,可能要20多分钟,而应用层通常都不会等待这么久的时间。
应用端是否启用了TCP keepalive特性。取决于应用层程序是否开启keepalive,具体是否开启,结合使用场景及性能考虑,其实原理都是一样的。
通过此次排查问题的经历,意识到,从 CPU 到内存、到磁盘、到操作系统、到网络,计算机系统处处存在不可靠因素。并且当达到一定业务量时,很多中间件业务节点,需要考虑调整及优化的地方。
No pain, no gain !
本文详细记录了一个关于应用系统访问Oracle数据库时出现连接超时的问题排查过程,最终通过调整TCP keepalive机制成功解决了问题。
1477






