ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PostgreSQL报错“An IO error occurred while sending to the backend”排查与解决

PostgreSQL报错“An IO error occurred while sending to the backend”排查与解决 1. 先搞清楚这个报错到底在说什么1.1 一条报错背后的完整链路做PostgreSQL运维和开发的人大概率都见过这么一条报错An IO error occurred while sending to the backend。我第一次见到它是在凌晨两点线上应用突然报错一堆告警飞过来应用日志里就这一句话当时真是头皮发麻。后来排查多了才发现这句话基本等于数据库客户端在说我要把数据发给服务端但没发出去或者发到一半通信断了。它不算PostgreSQL的致命错误但出现频率极高而且背后原因五花八门很容易让人走弯路。先聊一条报错在PostgreSQL客户端和服务端之间经历了什么。你写一行psql -h 10.0.0.5 -p 5432 -U app_user -d appdbpsql会先做TCP连接然后和服务端完成启动协议握手服务端发送认证请求客户端回应用户密码或者密钥接着协商参数最终进入可用的空闲状态。等真正执行SQL时客户端会通过本地的socket缓冲区把查询消息写到内核内核再通过TCP栈发送到服务端。也就是说send to the backend描述的是这个阶段客户端试图把数据查询文本、绑定参数、关闭连接请求等送达到PostgreSQL后端的socket上。如果在这个写的过程中本地socket不可写、对端已经RST、连接长时间超时客户端驱动就会抛这个IO异常。以JDBC为例PostgreSQL官方驱动org.postgresql.core.PGStream在send()或flush()时会写socket输出流一旦写入失败或返回错误码就会包装成 PSQLExceptionmessage就是这个。所以看到这个异常时第一反应不应该理解为数据库报错了而应该理解为数据库和应用之间的通道出问题了。数据库本身可能好好的只是这条通道断得莫名其妙。这也是为什么很多人查了数据库日志发现什么都没有日志里干干净净然后陷入迷茫。1.2 为什么是send而不是read from backend报错里写的是send to the backend不是read from the backend这个方向性很有价值。它说明错误发生在客户端请求发出的阶段而不是服务端响应回来的阶段。两者排查的重心完全不同。read from backend通常意味着SQL已经发出去了服务端也处理了但响应没回到客户端原因可能是网络不对称丢包、超时、服务端卡死或客户端读取缓冲溢出。send to the backend则多半发生在请求还没完全到达数据库之前排除了服务端执行SQL慢的情况重点怀疑的是网络连接本身失效、代理或负载均衡空闲断开、SSL握手后连接被重置等。注意一点PostgreSQL有时候会在服务端主动断开连接时让客户端在 send 阶段感知到比如服务端因为statement_timeout、idle_in_transaction_session_timeout或terminate_backend()强制断连客户端在发送下一条SQL时可能看到的是 send 报错也可能看到 connection reset 或 broken pipe具体取决于TCP状态和操作系统。所以send to the backend不能简单等同于客户端先坏服务端的行为一样能引发这个错误。我们需要带着这个认识去排查。2. 最常见的6个触发场景你对号入座2.1 网络夹层防火墙、负载均衡、云安全组悄悄捣鬼生产环境的PostgreSQL很少直连裸奔前面通常有安全组、防火墙、四层负载均衡、云数据库代理等。这些中间设备为了回收空闲资源往往会设置空闲连接超时。比如某云LB默认空闲超时可能是60秒或300秒如果应用连接池里的连接空闲超过这个时间中间设备会在不通知两端的情况下直接抹掉这条TCP连接。两端都不知道连接池里还躺着一条假活连接。等到应用从池里取出这条连接去执行SQL客户端往socket里写数据内核才发现对端早已不可达TCP重传几次后放弃于是直接报这个IO错误。这类场景有很明显的特征报错大多出现在一段空闲之后比如凌晨低峰后的第一个请求、定时任务每天跑的第一单、开发环境隔了一晚上第二天早上连不上。如果你发现报错时间和空闲时长强相关那八成就是中间链路空闲超时。排查时可以查LB日志、防火墙会话表或者直接在应用侧记录连接从池中取出的时间与上一次使用时间之间的间隔。更简单的验证方法是写一个测试程序故意让连接空闲N秒再执行查询看会不会复现。2.2 keepalive与空闲连接数据库比你想的更早放弃PostgreSQL本身有tcp_keepalives_idle、tcp_keepalives_interval、tcp_keepalives_count三个参数默认值通常继承操作系统的内核设置。Linux默认的tcp_keepalive_time很多是7200秒2小时也就是说一条空闲连接要等2小时才会第一次发keepalive探测。这个间隔远远大于很多网络设备或云LB的回收时间所以PostgreSQL自己的keepalive往往来不及探测连接就被中间设备抹掉了。更麻烦的是PostgreSQL服务端还有idle_session_timeoutPG14引入默认关闭但如果你开了并且设置得比较短空闲会话会被服务端主动关闭客户端再发SQL时同样会报send方向的IO错误。针对这种场景我给你两个方向一个是在服务端调小keepalive参数让数据库更快地探测死连接另一个是在客户端连接池层配置空闲检测比如HikariCP的maxLifetime和connectionTimeout、Druid的testWhileIdle和timeBetweenEvictionRunsMillis。用连接池定期淘汰空闲连接远比依赖操作系统TCP保活更可控。这一点在后面调优部分我会给出具体参数。2.3 SSL/TLS握手与协议协商失败很多人一看SSL就头大但PostgreSQL的报错里经常藏着一个SSL的坑。当pg_hba.conf里配置了hostssl并强制要求SSL连接时客户端在建立连接后、进行认证前会先做一次SSL握手。如果中间有防火墙或负载均衡对SSL流量做了拦截、改包、或者只放行了普通TCP端口但没放行SSL记录那么客户端发送SSLRequest包后可能等不到服务端的SSLResponse或者握手被RST中断。这种错误在客户端日志里不一定写得很细最终落给应用的就是这个send error。还有一种情况和证书相关服务端要求客户端证书sslcert或者服务端证书过期导致客户端校验失败。证书问题一般会带上明文错误但如果驱动版本较老、JDBC对SSL握手失败后的回退逻辑处理不好也可能笼统地报出send错误。排查SSL问题可以用psql加sslmoderequire或sslmodedisable分别试验再对比错误表现基本就能定位到是不是握手阶段。2.4 连接池与驱动侧的socket复用问题连接池是重灾区。以Java的HikariCP为例它默认的maxLifetime是1800000毫秒30分钟意思是连接最长存活30分钟就会被池子淘汰这个机制本身没问题。但如果你把maxLifetime设置得比数据库侧的会话超时或LB空闲超时长池子里的连接就可能已经在服务端或中间设备失效了。HikariCP还有一个keepaliveTime参数默认0不启用如果不启用池子只会在获取连接或归还连接时检查有效性而如果每次拿到的都是同一批空闲连接connectionTestQuery又没配置检测就会失效。此外老旧的驱动对socket半关闭状态处理有bug。特别是一些基于NIO的框架比如Netty、Vert.x在异步线程里写数据库socket一旦触发IOException如果没有正确关闭连接连接池里会残留坏连接下一轮继续被取出去写数据于是报错会连续出现。解决方式很简单在捕获到PSQLException且消息包含IO error时从连接池中剔除这条连接并强制重建。很多框架已经把这类异常标记为 fatal 错误但最好自己再验证一层别只靠默认逻辑。2.5 数据库端资源紧张导致的异常中断不要只怪网络数据库本身也可能成为断线的元凶。当PostgreSQL后端进程遇到严重的内存不足、磁盘IO卡死、CPU长时间100%时可能来不及优雅地发送错误消息直接终止了后端进程或者关闭socket。此刻客户端如果正往后端发送查询就会立即感知到IO异常。尤其是PG的并行查询或者大批量写入时如果shared_buffers、工作内存等配置不当某个backend进程被系统杀掉连接断开客户端看到的往往就是send报错而不是一个明确的SQL错误码。怎么判断是数据库端的问题看数据库日志。如果连接被终止通常会有日志记录比如server process (PID xxx) was terminated by signal 9: Killed或者connection received: unexpected EOF。如果数据库日志里出现了这类信息再去查dmesg看是不是OOM killer动了手。另一个隐蔽场景是磁盘满PostgreSQL在写入WAL或者数据文件时如果磁盘报错后端进程可能直接退出。这类问题在云数据库托管实例里不太常见但自建机房很容易踩到。2.6 客户端异常退出带来的半开连接最后一种情况在开发环境特别常见你写了一个快速测试脚本跑完没等连接关闭就 CtrlC 终止了进程或者IDE里直接 Stop导致客户端没有发送正常的Terminate消息只留下一个半开连接在数据库侧。下一次服务起来后如果复用了某些共享连接状态或者同一个端口新连接可能撞上残留的状态导致偶发IO错误。还有更简单的笔记本休眠再唤醒Wi-Fi切换虚拟网卡重置所有长连接瞬间失效。这类场景的特征是无法稳定复现报错集中在网络切换、睡眠唤醒、电脑重启后。对这类问题除了让连接池自动重建连接没有什么根治手段因为问题出在客户端所在主机的网络栈状态变化上。3. 从日志到复现一步步定位根因3.1 先看数据库日志排除后端主动断开报错一出现先别急着动应用。我的习惯是登录数据库节点打开PostgreSQL的日志文件。默认的日志路径在$PGDATA/log下文件名类似postgresql-2025-07-11_123456.log前提是logging_collectoron。用grep搜索报错时间点前后几秒的日志重点看这几类记录could not receive data from client、unexpected EOF on client connection、terminating connection due to administrator command、server process was terminated by signal。如果只有could not send data to client说明是服务端往客户端发响应时失败和报错方向相反不能直接挂钩。如果数据库日志完全干净那说明后端在TCP层什么都没感知到连接是被中间设备或操作系统静默丢弃的。这时候你的战场就不在数据库侧了而应该去抓客户端和服务端之间的链路。很多云数据库的控制台能看到连接数监控、网络包监控但有时候这些指标粒度太粗需要靠更底层的抓包来定位。3.2 客户端抓包或驱动日志判断是发送前还是发送中断抓包是终极手段但很多人一听tcpdump就慌。实际上数据库排查只需要最简单的抓法。假如客户端IP是192.168.1.20数据库IP是192.168.1.30报错瞬间执行tcpdump -i any -n host 192.168.1.30 and port 5432 -w /tmp/pg_io_error.pcap抓一段时间后用Wireshark打开pcap筛选tcp.flags.reset为1的包。如果看到客户端发了一个PSH包数据包后对端直接回了RST说明连接被服务端或中间设备拒绝。如果只看到客户端在反复重传TCP Retransmission没有RST则说明对端可能已宕机或路由不通数据包被黑洞。这里有个小技巧检查抓包里上一次通信到报错之间的时间间隔如果间隔远大于中间设备的空闲超时阈值那就破案了。如果不想上tcpdump也可以先开JDBC或psql的日志。JDBC可以在连接URL里加loglevel2对应org.postgresql.core.PGStream的日志配合loggerLevelTRACE会输出很多底层socket信息。psql没有这么细的日志但可以加-e显示发送给服务端的SQL配合stracestrace -f -e tracenetwork -p 进程号观察实际sendto()调用返回的错误码。ECONNRESET、EPIPE、ETIMEDOUT分别对应不同的故障方向这个信息很有用。3.3 最小化复现用一个psql命令来隔离问题定位网络问题时不要一上来就开几十个应用线程模拟。最干净的做法是找一个能直连数据库的机器用psql手动复现。比如psql host192.168.1.30 port5432 dbnameappdb userapp_user sslmoderequire connect_timeout5连接成功后什么也不做干等90秒再随便执行一条SQLselect 1;如果这个简单的操作也报同样错误说明链路本身有问题和应用代码无关。你还可以在psql里输入\conninfo查看连接参数确认用的IP、端口、SSL状态。如果psql一直稳定而应用每次报错那问题大概率在应用侧比如连接池设置、驱动版本、框架的socket处理、或者应用运行时的网络命名空间不同。还有一种高效手法写个小程序循环执行连接-空闲-查询不断缩短空闲时间找到报错的临界点。比如按0秒、30秒、60秒、90秒、120秒梯度测一旦发现超过某个阈值就必现中间设备的空闲超时参数基本就被试出来了。这个办法比看配置文档更准因为很多设备的实际行为连运维部门自己的文档都不一定准确。3.4 工具与参数速查表为了你排查时查阅方便我整理了一张速查表涵盖了我常用的排查命令和对应的观察点。这张表不是万能药但它能帮你把散落的操作系统诊断信息串起来至少不会在报错面前大脑空白。使用的时候注意每一条命令都要配合时间点去理解光看到某个连接状态是CLOSE_WAIT不算完还要确认这个连接是不是报错的那一条。抓包也一样抓到了不代表看得懂最好结合报错发生前后几秒的完整上下文。工具命令/参数观察点数据库日志grep -E could not|unexpected EOF|terminated|signal $PGDATA/log/*后端是否主动断连tcpdumptcpdump -i any -n host DB_IP and port 5432 -w /tmp/pg.pcap是否有RST、重传、半开连接ss/netstatss -tnp | grep 5432连接状态是ESTAB还是CLOSE_WAIT、TIME_WAITpsqlpsql host... connect_timeout5后执行\conninfo确认实际连接参数、SSL状态stracestrace -f -e tracenetwork -p 应用PID写入socket的系统调用返回值JDBC日志URL参数加loglevel2底层socket发送步骤的异常点这张表不全面但足够覆盖80%的场景。表里的每一项都值得单独研究尤其是ss输出的状态很多时候CLOSE_WAIT堆积直接说明了应用程序没有正确关闭socket。这个怪现象我见过不少次应用线程在一处抛异常后代码没有走finally里的close连接就永远躺在CLOSE_WAIT里。等你查的时候数据库端连接数还正常但客户端已经积累了几百个僵尸连接最终表现就是新连接建不上、老连接也发不了数据进而报出各种IO error。这类细节我在避坑部分还会重点展开。4. 修复方案与参数调优实操4.1 调整TCP保活参数让死连接早点暴露既然很多IO error是因为连接实际上已经死了但客户端不知道那就缩短探测周期。在PostgreSQL服务端可以给每个连接设置TCP keepalive参数不需要改内核全局配置。在postgresql.conf里tcp_keepalives_idle 60 tcp_keepalives_interval 10 tcp_keepalives_count 6含义是连接空闲60秒后开始发第一个keepalive探测包之后每10秒发一个连续6次没响应就判定连接死亡。这样最坏情况下一条死连接在6010×6120秒内会被服务端清理掉。这组参数对服务端主动感知客户端消失非常有效但它能解决的是数据库侧发现死连接并不能防止中间设备提前丢弃空闲连接。所以更靠前的手段是在连接池里做保活或淘汰。Linux内核全局参数也可以配合调整但影响面大不建议直接改生产服务器。如果你一定想改可以看sysctl net.ipv4.tcp_keepalive_time、net.ipv4.tcp_keepalive_intvl、net.ipv4.tcp_keepalive_probes但要注意会影响到所有TCP连接。服务端的PG参数是更加隔离的选择。4.2 连接池参数让应用主动管理连接生命周期连接池侧才是重头戏。拿 HikariCP 举例这是Java领域最常用的池子。推荐一套保守但有效的配置maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 5000 idle-timeout: 600000 max-lifetime: 1500000 keepalive-time: 30000 connection-test-query: select 1这里的max-lifetime我建议设置成比你网络链路空闲超时短的值。比如你知道LB 60秒回收空闲连接那max-lifetime就要小于60秒否则连接在池中活过60秒就被LB杀了。但max-lifetime太短会导致频繁重建连接对数据库产生额外的认证压力所以要平衡。keepalive-time可以设置成30秒让池子每30秒主动向空闲连接发送一个探活查询比如select 1这样LB还没到回收时间连接就又活跃了一次就不会被判定为空闲了。注意HikariCP的 keepalive 只在连接空闲超过该时间且小于 max-lifetime 时才生效不是每隔30秒无脑发理解一下。Druid用户也很常见对应的参数是timeBetweenEvictionRunsMillis、testWhileIdle、validationQuery原理类似。核心思想都是定期给连接喂一次心跳让中间设备认为连接一直是活跃的同时快速剔除干脆已经坏掉的连接。另外强烈建议在获取连接时不要设置testOnBorrowtrue因为每次借用都执行一次探活查询会增加几毫秒延迟高并发下会放大除非你的场景对极端一致性要求非常高。实际调优时先用testWhileIdle加合理的检测间隔大多数问题都能解决。4.3 修正pg_hba.conf和SSL相关配置如果排查到SSL握手环节先确认pg_hba.conf里的规则。比如hostssl all all 0.0.0.0/0 scram-sha-256这段表示只允许SSL连接。如果你有些客户端工具没配SSL连上来就直接失败。建议在postgresql.conf里调整ssl on ssl_cert_file server.crt ssl_key_file server.key ssl_min_protocol_version TLSv1.2同时把pg_hba.conf里的规则尽量精细化内网走host明文外网或跨网段走hostssl这样能减少很多SSL握手带来的问题。还要注意ssl_ciphers别设置得太旧或太新部分中间设备对特定密码套件处理异常。遇到握手报错可以临时把某个客户端的pg_hba规则改成host去掉ssl来对比测试但别长期在生产裸奔这只是一个定位手段。另外很多云数据库实例提供SSL加密开关如果应用侧开了sslmodeverify-ca或verify-full需要正确配置CA根证书路径和证书主机名校验。有一点容易被忽略PostgreSQL的SSL证书必须包含CN或Subject Alternative Name为数据库主机的IP或域名否则verify-full模式一定失败。这种失败在不同驱动里表现不一样可能直接抛出IO error也可能明说证书校验失败。如果你用的是老驱动建议至少升级到当前主流大版本比如 PG JDBC 42.x里面修了很多SSL握手细节。4.4 云环境下的安全组与LB空闲超时配置自建数据库在机房网络链路相对可控。但如果你用的是云数据库RDS PG或者前面加了SLB/NLB就要去云控制台查连接超时配置。以常见的负载均衡为例四层监听通常有连接空闲超时和连接最大空闲时间默认可能只有几十秒对数据库这种需要长连接的场景非常不友好。我踩过一个很典型的坑某产品通过NLB连PostgreSQLNLB空闲超时默认是310秒应用连接池最大空闲时间设置为300秒理论上没问题但实际数据库服务端tcp_keepalives_idle还是默认值三方时间一叠加某个凌晨的定时任务正好在空闲了320秒后发查询中间设备早已斩断连接结果就是一大波An IO error occurred while sending to the backend。修改方式很简单在云控制台的NLB/TGW/SLB配置里把空闲超时调到15分钟以上或者干脆开启长连接模式。安全组层面确保数据库端口只对可信源开放但又不能太严导致健康检查失败。云厂商的数据库服务一般还提供连接数和活跃连接数监控如果发现活跃连接数在中低负载下突然掉到0而应用没有重启那多半是中间连接被切断此时去查网络链路配置肯定有收获。5. 避坑指南与经验沉淀5.1 我踩过的几个坑第一个坑只改客户端超时不改服务端keepalive。之前有一回应用频繁报IO error我先把HikariCP的idle-timeout调成了60秒期望尽快清理空闲连接结果一上线连接数暴涨数据库连接创建开销巨大反而加剧了负载。后来才意识到池子里的连接数量少频繁淘汰重建和频繁创建其实没有本质区别而且idle-timeout只对低于minimum-idle的额外空闲连接生效你调小了它池子并不会把所有空闲连接都杀掉。正确的做法是先搞清楚是服务端主动断的还是中间设备断的再决定改哪一侧参数。第二个坑忽略statement_timeout和事务超时。有一次系统报这个IO error查网络、查连接池都没问题最后在数据库日志里看到大量的canceling statement due to statement timeout。原因是一个慢查询被statement_timeout杀掉后客户端正在等待结果于是连接关闭客户端再发下一条SQL时直接报send错误。实际上这并不是网络问题是SQL和超时参数的问题。所以排查IO error时一定要把数据库侧的日志和慢查询日志一起看别只顾着抓包。第三个坑连接池里的坏连接没有及时剔除。使用某些老旧的中间件或者自定义连接池时如果没实现fatal异常即时剔除逻辑一次断线后池子里所有连接都变成坏的应用会连续报错直到连接池全部重建。这个问题在Druid上可以通过配置exceptionSorter解决在HikariCP上通常会自动识别PG的fatal错误但如果你用了包装异常识别就可能失效。最好在调用数据库的catch块里加一个判断如果异常信息包含IO error就执行evictConnection()或者直接从池里移除。5.2 监控与告警配置建议一个成熟的PostgreSQL应用不应该只在用户报障时才发现连接断了。建议至少配置三块监控应用侧连接池监控、数据库侧连接数与会话状态监控、网络层TCP连接监控。应用侧HikariCP通过Micrometer可以暴露hikaricp_connection_timeout_total、hikaricp_connection_creation_total、hikaricp_connections_pending等指标如果connection_timeout_total突然上涨往往伴随大量IO error这时候告警出来就说明连接池在大量丢连接。数据库侧看pg_stat_activity里state为idle in transaction的数量这个值异常升高时事务长期挂起容易导致连接被中止。网络层不需要太复杂的系统定期从应用所在机器探测数据库端口即可但注意能连通不代表应用连接池里的连接是新鲜的最好做成建连-查询-断开的活性探测。告警阈值可以按你的SLO来定如果IO error一分钟内超过5次就page到人了。但也要注意一些后台定时任务本来就是低峰期执行可能出现一次性几十条错误所以还需要结合时间段和错误持续时间做判定避免深夜把值班人从被窝里拽出来。5.3 给新手的排查checklist如果你刚接手这类问题不知道从哪里下手按照这个顺序排查成功率最高。这个顺序不是凭空拍脑袋定的而是根据故障发生概率和排查成本排出来的先看数据库日志成本最低往往能直接定位再看连接池参数和应用行为能覆盖大多数场景最后才上升到抓包和驱动版本因为这两步需要工具支持有的公司甚至不允许在核心节点执行 tcpdump。每一步如果没结论就别硬往下一步走回头再审视上一步的数据多半能找到线索。打开数据库日志搜索报错时间点确认是否有后端主动断开的信息。确认报错方向是send先排除SQL执行本身的问题比如长事务、statement_timeout、被kill。查看连接池的池化参数重点看maxLifetime、idle-timeout、探活机制是否开启。确认应用与数据库之间是否有LB或防火墙或安全组并查它们的空闲超时时间。用psql手动模拟连接后空闲N秒再查询二分法找出临界空闲时间。如果方便在客户端侧抓包看TCP重传与RST方向。更新驱动到当前主线大版本排除旧驱动bug。修复后把连接池对坏连接的剔除逻辑打开并配置监控。这套流程基本能让大多数IO error在半天内找到根因。你不需要一开始就精通TCP但要把常见可能性心里有数然后按图索骥。6. 实战案例复盘从告警到止血的40分钟6.1 案例背景与现象去年我负责的一个业务模块某个早上连续收到告警内容是一批定时任务失败错误清一色都是An IO error occurred while sending to the backend。定时任务每天凌晨4点启动大概跑10分钟之前一直正常但那天突然全部失败。我先查了数据库日志却什么可疑记录都没有。再看告警时间点任务启动前数据库连接池已经建立了连接但池子里连接最后一次使用是前一天晚上10点距离任务执行大概6小时。中间没有任何流量而云LB的空闲超时设置恰好是4小时。这就高度疑似LB或者中间设备把连接回收了但因为连接池一直保留着这些连接所以应用在发第一条SQL时就感知到send失败。6.2 排查过程与处理方式我去了云控制台查那个NLB监听的空闲超时果然显示3600秒表面上看起来不小但仔细一算从晚上10点到凌晨4点有6小时3600秒只有1小时早就超了。再查数据库侧PG参数tcp_keepalives_idle是默认值意味着服务端预计2小时才发一次探测而NLB在1小时就把连接干了PG根本来不及发现。两个环节一合计问题就清楚了。处理方式先把NLB空闲超时调到9000秒同时把HikariCP的max-lifetime缩短为240分钟并开启keepalive-time60s让连接池每60秒发一次探活保证连接永远新鲜。另外把数据库侧tcp_keepalives_idle也调到120秒双管齐下。改完后第二天定时任务就安静了之后再没出现这种报错。6.3 复盘为什么4点这个时间点容易出事复盘时我意识到一个容易被忽略的细节这类IO error不一定发生在长时间空闲后的第一笔请求也可能发生在低峰期任务突然并发时。因为连接池里保留了一堆被中间设备杀掉的连接任务并发启动时大量线程同时从连接池取连接然后几乎同时write一瞬间触发一堆RST数据库连接池的连接数监控可能会看到断崖。如果你把max-lifetime设置得太长这种故障还会反复出现。所以定时任务型的应用最好在任务启动前主动做一次连接池预热用一个简单的查询确认连接可用或者在任务代码里显式剔除不可用连接。很多框架的定时调度器支持任务前回调在里面执行一次connection.isValid(2)就行。最后再分享一个个人习惯我会在应用日志的上下文里记录每次数据库操作里连接的使用详情比如连接在池中的创建时间、最后归还时间、当前操作耗时。这样一旦发生IO error能立刻从日志上下文里看出这条连接的年龄和空闲情况不需要临时再查。排查网络问题很多时候就像侦探破案现场信息越多越不容易走弯路。
返回列表