连接异常排障手册:拒连、超时、认证失败、空闲断开
连不上数据库这件事,表面上都是一句"连不上",但背后的原因差别很大。有时是网络根本不通,有时是端口没开,有时是数据库进程压根没在监听,有时是认证规则挡住了,有时就是账号密码填错了,还有一种更隐蔽------连接池里的旧连接已经被中间网络设备悄悄断开了,但应用还以为它是好的。
排障要按层推进,不要一上来就改应用代码。 
@toc
一、先把连接链路拆开
咱们连一条数据库的链路。拆开来看的话。大概是这样的:
text
应用进程
-> JDBC 驱动
-> Windows 11 网络
-> 防火墙或安全组
-> CentOS 7.6 服务器端口
-> 数据库监听
-> 认证规则
-> 用户密码
-> 数据库和对象权限
每一层都是有可能出问题的。那排查的时候呢。咱们从网络这一层开始。一层一层往上推。不要跳着去查。
二、Connection refused
这可以说是最常见的一种了。这个报错的意思是啥呢。就是目标主机其实是可达的。但是目标端口上。没有服务来接受你的连接。或者说连接被直接给拒绝了。
那咱们先在 Windows 11 这端测一下端口:
powershell
Test-NetConnection 192.168.10.101 -Port 54321
接着到 CentOS 7.6 那端去看看监听情况:
bash
ss -lntp | grep 54321
再确认一下数据库进程在不在:
bash
ps -ef | grep kingbase | grep -v grep
那可能的原因有哪些呢。可能是数据库服务没启动。也可能是实际端口不是 54321。或者数据库只监听了本机地址。那防火墙或安全组拦住了的情况也是有的。还有就是应用里的 IP 填错了。
排查顺序的话。咱们先确认 IP 和端口。接着看数据库进程在不在。然后再看端口监听。再接着看防火墙。最后去检查数据库监听地址。看看它是不是允许远程访问。
三、Connection timed out
超时和拒连它不一样的。拒连的话。是你到了门口。门关着。那超时呢。往往仅仅只是找不到门在哪里。你的请求发出去了。但是一直没有回应。
powershell
ping 192.168.10.101
Test-NetConnection 192.168.10.101 -Port 54321
要是 ping 不通但端口能通。那你就继续查数据库。要是端口也不通的话。那就得先解决网络和安全策略的问题了。
咱们在 CentOS 端检查一下防火墙:
bash
sudo firewall-cmd --list-ports
接着放行端口:
bash
sudo firewall-cmd --zone=public --add-port=54321/tcp --permanent
sudo firewall-cmd --reload
四、认证失败
认证失败的话。通常来说。会出现 FATAL 啊。密码错误啊。用户不允许访问这类的信息。
咱们先手工确认一下连接参数:
text
host=192.168.10.101
port=54321
database=kb_app
user=app_user
然后从 Windows 11 那边。用 ksql 手工连一次看看:
cmd
ksql -h 192.168.10.101 -p 54321 -U app_user -d kb_app
要是 ksql 也连不上的话。那就说明不是 Java 代码的问题了。是你的配置本身就有问题。
那排查点有这些。用户名或者密码是不是写错了。数据库名对不对。账号是不是被锁定了。还有认证规则是不是允许 Windows 客户端连过来。再看看应用是不是加载了错误的 profile。用了别的环境的密码。
五、工具能连,应用不能连
这类问题啊。往往仅仅只是因为应用配置和工具配置没对齐。咱们得逐项来对比一下:
| 项目 | 工具配置 | 应用配置 |
|---|---|---|
| IP | 是否一致 | 是否一致 |
| 端口 | 是否一致 | 是否一致 |
| 数据库名 | 是否一致 | 是否一致 |
| 用户名 | 是否一致 | 是否一致 |
| 密码 | 是否一致 | 是否一致 |
| Schema | 是否一致 | 是否一致 |
| 驱动版本 | 工具内置 | 应用依赖 |
Spring Boot 里面的话。咱们重点看当前激活的是哪个 profile:
text
--spring.profiles.active=dev
要是应用实际加载的是 test 或者 prod 的配置。那连接目标就变了。这个点其实是很容易被大家漏掉的。
六、应用启动成功,但第一次请求很慢
应用起来了。但是第一个请求却很慢。这种情况其实不少见。那原因在哪呢。通常来说是因为连接池懒加载。也就是说第一次请求的时候。它才真正去建连接。另外的话。DNS 解析慢啊。或者数据库首次执行 SQL 需要缓存或编译。也会造成这个现象。那要是连接池最小空闲连接设了 0 的话。这个问题就更明显了。
我建议啊。咱们可以增加个启动自检。在应用启动阶段就去执行一下:
sql
SELECT current_database(), current_user;
把建立连接这个事。前置到启动阶段去搞。那第一个用户请求。就不会吃到这个延迟了。
七、运行一段时间后连接断开
这种断开啊。往往是悄悄发生的。网络设备啊。防火墙啊。或者数据库侧的空闲连接策略。会把长时间不活动的连接。给它静默断开了。问题是什么呢。问题是连接池不知道这个事。它还以为那个连接是好的呢。等应用拿出来用的时候。一执行 SQL 才报错。这是一个问题。
那处理思路是啥呢。咱们可以设置一个合理的 max-lifetime。让连接池主动把老连接给换掉。这个 max-lifetime 一定要小于网络设备的空闲断开时间。同时还要设置一下校验超时。
咱们看个 HikariCP 的示例:
yaml
spring:
datasource:
hikari:
max-lifetime: 1800000
idle-timeout: 600000
validation-timeout: 2000
要是中间设备 30 分钟断开空闲连接的话。那你的 max-lifetime 就要设得比 30 分钟短。比如设成 25 分钟。也就是 1500000 毫秒。
八、偶发获取连接超时
要是日志里出现:
text
Connection is not available, request timed out
这就说明。你从连接池里拿不到连接了。这个时候。不要直接去调大 maximum-pool-size。很多时候啊。问题往往仅仅只是不是连接数量不够。而是连接被慢 SQL 或者长事务给占住了。释放不回来了。
那咱们排查啥呢。看看连接池是不是太小了。有没有慢 SQL 堆积。有没有长事务一直开着不关。有没有连接泄漏的情况。是不是有定时任务在抢占连接。还有应用扩容后。总连接数是不是超预算了。
咱们可以在数据库侧看看活跃的 SQL:
这里说明一下哈。运行状态视图。你得看你实际用的 KingbaseES 版本为准。要是字段名不一样的话。你就按"会话 ID、用户、客户端地址、状态、SQL 开始时间、SQL 文本"这几个字段去替换一下就行了。
sql
SELECT pid, usename, client_addr, state, now() - query_start AS running_time, query
FROM sys_stat_activity
WHERE state = 'active'
ORDER BY running_time DESC;
等你拿到慢 SQL 之后。再去判断到底是调连接池呢。还是去优化 SQL。
九、权限不足和对象不存在
要是连接成功了。但是执行 SQL 才报权限不足或者对象不存在。这就不是连接的问题了。这是权限或者 Schema 的问题了。
咱们先确认一下当前的身份:
sql
SELECT current_database(), current_user;
接着确认一下你的 SQL 里写没写 Schema:
sql
SELECT * FROM shop.t_connection_check;
那要是写成这样:
sql
SELECT * FROM t_connection_check;
那就可能因为默认 Schema 不一致。找不到对象了。也就是说。应用 SQL 里。咱们显式写上 shop. 前缀。能省掉很多这类麻烦。
十、排障顺序总表
| 现象 | 优先检查 |
|---|---|
| Connection refused | 进程、端口监听、防火墙、监听地址 |
| Connection timed out | 网络、路由、安全组、防火墙 |
| 认证失败 | 用户名、密码、数据库名、认证规则 |
| 工具能连应用不能连 | profile、URL、驱动、账号 |
| 运行一段时间断开 | 连接生命周期、空闲断开、网络设备 |
| 获取连接超时 | 连接池、慢 SQL、长事务、泄漏 |
| 对象不存在 | Schema、权限、目标数据库 |
十一、小结
连接异常这个事啊。咱们得分层来排查。先去证明网络和端口通不通。接着证明数据库监听对不对。再证明认证和账号行不行。最后再去看应用配置啊。连接池啊。还有对象权限这些。也就是说。按链路一步步推进。大多数"连不上"的问题。都是能快速定位的。
那下一篇呢。咱们就看看 KES Studio 和 DMS 这类工具。是怎么辅助开发管理的。让咱们接入的过程啊。不只依赖命令行和日志。