数据库连接频频报错,排查思路全在这了。干运维这些年,凌晨三点被电话叫醒的次数,一半以上都是数据库连接问题。客户那边急得像热锅上的蚂蚁,你这边打开终端,对着满屏的报错日志,脑子一片空白。别慌,这事儿有套路。我见过太多同事一上来就冲进代码里查连接池配置,折腾半天发现是网络防火墙的事。所以今天我把这些年踩过的坑、总结的路子全摊开讲,你照着这个顺序排查,能少走一半弯路。

第一件事,先看报错信息本身。很多人一见到"Connection refused"就懵了,其实这几个单词已经告诉你答案了——连接被拒绝,说明对方端口根本没在监听,或者防火墙把包拦了。但如果是"Connection timed out",那就是网络层面的问题,包发出去了,石沉大海。还有一类是"Too many connections",这更直白,就是连接数满了。我见过最离谱的一次,报错信息是"Connection reset by peer",结果查了半天,是对方数据库主动把连接掐了,因为客户端那边有个长连接空闲太久没心跳。所以拿到报错,别急着百度,先把错误码和完整堆栈贴到文档里,逐字读三遍。报错信息里往往藏着最直接的线索,比你看十篇博客都有用。
第二步,立刻检查数据库服务状态。这不是废话,是真的有人会跳过这一步。我处理过一个case,开发说数据库连不上,我远程一看,服务器都宕机了。你可能会笑,但真遇到生产环境告警,人一慌就容易忽略最基本的东西。用systemctl status或者ps -ef grep 3306。如果服务正常,就去看日志,MySQL的error log、PostgreSQL的pglog,里面会记录详细的错误原因。有一次我排查半天,在日志里看到一句"Can't start server: Bind on TCP/IP port: Address already in use",原来是端口被别的进程占了,改个端口立马恢复。记住,数据库自己不会说话,但它会把所有异常都记在小本本上。
第三步,检查网络连通性。这一步很多人会忽略,因为觉得开发环境没问题,生产环境就应该没问题。但生产环境的网络策略往往更严格,安全组、防火墙规则、子网路由,随便哪个配置错了,连接就断。我教你一个笨但有效的办法:在应用服务器上telnet数据库IP的端口,比如telnet 192.168.1.100 3306,如果通了,会看到类似"Connected to"的提示。如果不通,再用ping测一下IP通不通,如果IP通但端口不通,那基本就是防火墙拦截了。这时候别犹豫,直接找网络管理员要白名单配置,把应用服务器的IP加进去。我还遇到过更隐蔽的情况,数据库和应用在同一个VPC里,但一个用了公网IP,一个用了内网IP,结果路由对不上,连接时好时坏。
第四步,看连接池配置和应用代码。这一块坑最多,也最隐蔽。连接池大小设置不合理,太小了并发一高就报"Connection pool exhausted",太大了又把数据库拖垮。我见过一个项目,连接池最大设了200,但数据库maxconnections才150,一上线就崩。还有连接池的空闲回收时间,设得太短,连接不断重建,数据库那边频繁握手,CPU飙升;设得太长,空闲连接占着资源不释放。代码层面也要查,有没有在finally里正确关闭连接?有没有把连接放进ThreadLocal里导致线程间共享?我处理过一个经典bug:代码里用了静态变量存Connection,结果多线程环境下,一个线程关了连接,另一个线程还在用,直接报"Connection is closed"。这种问题,光靠看日志很难定位,得结合代码review和线程dump一起看。
第五步,检查数据库自身的资源瓶颈。连接报错很多时候是表象,根子在数据库快撑不住了。连接数打满,可能是慢查询太多,每个查询都占着连接不放。这时候你去查performanceschema或者pgstat_activity,看哪些SQL在长时间运行。还有内存不足,数据库OOM了,连接直接被kill。我碰到过一次诡异的故障:每天早上九点准时报连接错误,过了十点又自动恢复。排查了三天,发现是定时任务在九点跑一个大报表,把内存吃光了,导致连接全被拒绝。所以看到连接报错,别只盯着连接本身,要看看数据库的CPU、内存、磁盘IO是不是有异常波动。监控系统如果没配,赶紧补上,不然下次还得靠猜。
第六步,别忽略应用服务器和数据库之间的中间层。比如用了HAProxy、Nginx做负载均衡,或者用了云数据库的代理,这些中间件也可能成为瓶颈。连接数打满,可能是代理层配置的连接数上限太低。超时时间设得不合理,会导致后端连接被提前断开。我处理过一个case,应用和数据库之间隔了一层云数据库代理,代理默认的连接空闲超时是60秒,而应用那边的连接池空闲保活时间设了120秒,结果一到60秒,代理把连接断了,应用还傻乎乎地复用,直接报错。这种问题,需要同时看应用日志、代理日志和数据库日志,三方对照才能发现时间线不一致的地方。
一条,也是最重要的一条:建立完善的监控和告警体系,别等问题爆发了才去救火。连接数、活跃连接数、等待连接数、连接建立耗时、错误率,这些指标全部要上监控。阈值设好,比如连接数达到80%就开始告警,别等到100%才通知你。我见过太多团队,平时不配监控,出了事才手忙脚乱地查日志。而且告警要分级,连接数告警是P2,但如果是数据库宕机,那就是P1,直接电话打过来。有了监控,你还能看到趋势,比如连接数每天下午两点开始涨,那你就知道是业务高峰期到了,提前扩容或者优化SQL。数据库连接这事,治标不如治本,治本靠的就是数据说话。
数据库连接报错这事儿,说复杂也复杂,说简单也简单。复杂在于链路长,从应用代码到连接池,到网络,到中间件,再到数据库本身,任何一环出问题都会报错。简单在于,只要你有条理,一层一层往下查,总能找到根因。我见过太多同事,一遇到连接报错就慌了神,东改一下西调一下,问题没解决,还把环境搞得更乱。所以记住这个顺序:报错信息、服务状态、网络连通、连接池配置、数据库资源、中间层、监控体系。每一步都做扎实了,数据库连接报错就再也难不倒你。下次再有人半夜打电话喊你,你可以气定神闲地打开终端,一步步来,然后用实力告诉对方:这事儿,稳了。


