Victor's Code Journey
Victor's Code Journey

目录

目录

HikariCP Connection Adder 被驱动 Socket Read 卡死:故障复盘与排查手册

目录

先看一段栈,再决定要不要继续读——

# 这两份 stack dump 间隔 14 分钟抓的,"jdbc:mysql://10.14.127.31:19030/xxx"
# 的 connection adder 线程,tid 完全一致,栈帧一帧没动。
# 业务侧呢?告警群里全是 SQLTransientConnectionException。

说真的,这种栈谁都不想看到——10 个 HikariCP 连接池、每个池的 adder 都卡在 socketRead0,不是忙,是死等。更扎心的是,业务方第一时间甩锅 HikariCP"卡死了",但 HikariCP 的 MAIN 池好好的;另一边 DBA 看到 MySQL 进程没崩、SHOW PROCESSLIST 也查不出明显异常。两边都没事,问题到底出在哪?

这篇文章就是讲这次排查过程的:怎么从两份 dump 锁定到驱动层握手 read 死等,怎么排除掉一堆看似合理实则救不了命的客户端调参,以及 HikariCP 4.x 的 async-init 这次为什么没救成。文末我会留一份"如果你也在用 5.1.47,请按这个 checklist 立刻自检"的清单,能直接拿走。

如果你的项目里还在跑 mysql-connector-java < 5.1.49强烈建议你先跳到 §6 的 Q&A 把 Q7 读完——它会告诉你为什么"加 socketTimeout"这个直觉做法在这次故障里完全没用。

  • 现象:应用 10 个 HikariCP 连接池同时失效,业务侧持续报 SQLTransientConnectionException: Connection is not available, request timed out
  • 直接卡点:每个池的 connection adder 线程均卡在 socketRead0,两份 jstack 间隔 14 分钟、同一组 tid、同一帧栈。
  • 根因mysql-connector-java 5.1.47 在新建连接握手阶段的 socket read 永不返回;叠加 HikariCP 单 adder 同步执行 newConnection() 的架构,被服务端瞬时异常扇出成大故障。
  • 修复:升级驱动到 5.1.49 或 8.x;HikariCP 升 4.x 已做 async-init,但本故障死等发生在 init-async 切出前的握手 read 阶段,必须配合驱动升级才能根治。
  • 预防:驱动层 socket timeout、服务端可用性、应用层 watchdog 三层联动;任意一层单独加固都无法覆盖该故障。
时间事件
T010 个业务连接池(g_caiwu_stream / soda_*_stream / …)陆续报 Connection is not available, request timed out after 30000ms
T0+7min第一次 jstack(stack.log),10 个 adder 全部卡死
T0+21min第二次 jstack(stack1.log),与第一次同一组 tid、同一帧栈
T0+30min重启客户端服务,恢复
  • 受影响池:10 个(同一 MySQL 端点 10.14.127.31:19030
  • 未受影响池:MAIN_HikariCP——说明问题在业务数据源侧,不在 HikariCP 框架本身
"jdbc:mysql://10.14.127.31:19030/pool-A?... connection adder" #7952751 daemon
   java.lang.Thread.State: RUNNABLE
       at java.net.SocketInputStream.socketRead0(Native Method)
       at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
       at java.net.SocketInputStream.read(SocketInputStream.java:171)
       at java.net.SocketInputStream.read(SocketInputStream.java:141)
       at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:101)
       at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:144)
       at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:174)
       at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3011)
       at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3472)
       at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3462)
       at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3905)
       at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2530)
       at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2683)
       at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2491)
       at com.mysql.jdbc.ConnectionImpl.loadServerVariables(ConnectionImpl.java:3797)   ← 卡点
       at com.mysql.jdbc.ConnectionImpl.initializePropsFromServer(ConnectionImpl.java:3230)
       at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2243)
       at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2025)
       at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:778)
       at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:47)
       ... 反射构造 ...
       at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:330)
       at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138)
       at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:364)
       at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:206)
       at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:476)
       at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:726)

少数 pool 走的是另一条握手路径 setupServerForTruncationChecks(被 jdbcCompliantTruncation=true 触发),栈底完全一致。

两份 stack dump 间隔 14 分钟,10 个卡住的线程 tid / nid / 栈帧完全一致。这是判断"阻塞"而非"繁忙"的关键证据:

  • RUNNABLE + socketRead0 不代表正在执行,仅说明 OS 调度层面可运行。
  • 14 分钟栈帧未移动、且多个池同步发生——可以判定为阻塞在 syscall。
┌─────────────────────────────────────────────────────────────────┐
│ 应用层:HikariCP connection adder 被驱动同步阻塞                  │
├─────────────────────────────────────────────────────────────────┤
│ 驱动层:mysql-connector-java 5.1.47 握手阶段 socket read 无超时  │
├─────────────────────────────────────────────────────────────────┤
│ 服务端:MySQL 在握手阶段对新建连接不响应(或响应极慢)             │
└─────────────────────────────────────────────────────────────────┘

握手阶段死等的调用栈:

MysqlIO.readFully
  → ReadAheadInputStream.fill
  → SocketInputStream.read
  → socketRead0 (native, 阻塞)
参数在握手阶段是否生效(5.1.47)原因
JDBC URL connectTimeout生效但卡点不在 TCP 三次握手
JDBC URL socketTimeout不生效5.1.x 握手阶段走 MysqlIO.readFully → ReadAheadInputStream.fill,该路径不传递 setSoTimeout
Statement.setQueryTimeout无效连接尚未建立
tcpKeepAlive=true默认关闭,探测周期过长5.1.49 之前默认 false;Linux 默认 tcp_keepalive_time=7200

只有升级到 5.1.49 或 8.x(com.mysql.cj.*)才能修复该问题——8.x 重写了握手代码路径,socketTimeout 才能真正打断该 read。

HikariCP 配置是否能中断握手 read原因
connectionTimeout控制业务 getConnection() 的等待时长,不涉及驱动建连过程
validationTimeout仅用于探测已有连接
keepaliveTime / maxLifetime用于驱逐存量连接
initializationFailTimeout仅在启动期生效
4.x async-init部分缓解仅将"握手后"逻辑切出主线程,握手 read 仍在 newConnection() 内,adder 仍同步阻塞

关键结论:任何 Java 层超时都无法中断 native socketRead0——线程阻塞在 syscall,Java 层没有机会检查中断标志。

这 10 个池全部指向同一 MySQL 端点 10.14.127.31:19030。故障链大致是这样的:

  1. 池里现连接被服务端踢除(例如 wait_timeout 过期、KILL 连接),或 HikariCP 因为 maxLifetime 主动驱逐。
  2. 池瞬间需要批量重建连接,多个池的 adder 同时向 MySQL 发起握手。
  3. 某个服务端诱因在同一时刻被踩中——max_connections 打满、主从切换、VIP 漂移、磁盘 IO 抖动等,导致新握手在服务端被排队或丢包。
  4. 所有 adder 上的握手 read 全部死等,10 个池同步卡死。

服务端常见诱因(按出现概率从高到低):

  1. max_connections 打满:新握手被排队,客户端 read 死等。
  2. DNS 反向解析慢:gethostbyaddr 使服务端卡在 login 阶段。
  3. 主从切换 / VIP 漂移:包被发往旧主后丢弃,客户端 read 永不 ACK。
  4. 磁盘 IO 抖动:MySQL 握手线程被 IO 调度阻塞。
  5. LB / 防火墙会话表过期:握手包过去、回包被丢弃。
维度#2161本次故障
根因同类:adder 卡驱动握手 socket read同类
驱动Oracle 18.3.0.0MySQL Connector/J 5.1.47
阻塞阶段Oracle T4C logonMySQL loadServerVariables / setupServerForTruncationChecks
HikariCP 能否规避3.4.5 不能4.0.3 部分缓解,仍阻塞
临时止血手段oracle.jdbc.ReadTimeout(实测无效)socketTimeout(同样无效)
终结方案升级 HikariCP / 驱动升级驱动到 5.1.49 / 8.x + 排查服务端

故障定位的核心思路就一句话:先排除"是不是卡在驱动 socket read 上",再决定下一步是改客户端还是找 DBA。下面这一节是按这个思路写的可复用 SOP。

判断"HikariCP 连接池被打挂且是 adder 卡驱动"的三项特征:

  • stack dump 中 XxxDataSource connection adder 线程处于 RUNNABLE,但栈底位于 socketRead0 / read0
  • 多次 dump(间隔 ≥5 分钟)这些 adder 的 tid、栈帧完全一致
  • HikariPool housekeeper 报 Pool stats (total=0, active=0, idle=0, waiting=N) 并伴有 Add connection elided 日志

任一满足即可按下文继续排查。

应用侧(RD / 中间件):

  • 依赖版本:HikariCP / mysql-connector-java / JDK
  • HikariCP 配置:maximumPoolSize / minimumIdle / connectionTimeout / validationTimeout / keepaliveTime / maxLifetime / initializationFailTimeout
  • JDBC URL 完整参数(重点关注 useServerPrepStmts / cachePrepStmts / jdbcCompliantTruncation / connectTimeout / socketTimeout / tcpKeepAlive
  • 故障窗口两次 jstack,间隔 ≥10 分钟(用于对比 tid / 栈帧是否一致)
  • HikariPoolMXBean 的 active / idle / waiting / total 时间线
  • SQLTransientConnectionException 首次出现的时间戳

DBA / 服务端侧:

  • 故障 MySQL 的 max_connections / Threads_connected / Threads_running 峰值
  • SHOW PROCESSLIST 中是否存在 unauthenticated user | login 堆积
  • MySQL error log:是否存在连接拒绝、OOM、KILL 事件
  • 主从状态:是否存在切换、relay log 损坏
  • 磁盘 IO:故障窗口的 iostat / iotop
  • 网络:是否存在 VIP 漂移、交换机异常、防火墙事件
  • skip-name-resolve 是否已开启

Step 1. 客户端抓包 — 30 秒内可区分服务端与驱动的责任边界

tcpdump -i any -nn -s 0 host 10.14.127.31 and port 19030 -w /tmp/dump.pcap

配合 Wireshark 查看握手 read 是否有回包:

  • 有回包 → 驱动未正确解析(升级驱动)
  • 无回包 → 服务端未响应(继续 Step 2)

Step 2. 服务端确认 — 由 DBA 执行

-- 快速看连接是否被打满、是否有未完成握手
SHOW VARIABLES LIKE 'max_connections';
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Threads_running';
SHOW PROCESSLIST;                          -- 重点观察是否存在 unauthenticated | login 堆积
SHOW VARIABLES LIKE 'skip_name_resolve';   -- 未开启 = 握手阶段会做 DNS 反查,慢/挂会拖死连接

-- 故障窗口的 SQL 聚合(需 performance_schema 开启;<故障窗口> 替换为实际起止时间戳)
SELECT *
FROM performance_schema.events_statements_summary_by_digest
WHERE first_seen BETWEEN '2026-08-11 20:30:00' AND '2026-08-11 21:30:00'
ORDER BY count_star DESC
LIMIT 20;

Step 3. 应用层止血 — 不依赖根因修复

import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.HikariPoolMXBean;

public void evictStuckPool(HikariDataSource ds, String reason) {
    HikariPoolMXBean mx = ds.getHikariPoolMXBean();
    // 仅在池已失效时驱逐,避免误伤健康池
    if (mx.getTotalConnections() == 0
            && mx.getIdleConnections() == 0
            && mx.getThreadsAwaitingConnection() > 0) {
        mx.softEvictConnections();
        log.warn("Evicted stuck Hikari pool '{}', reason={}", ds.getPoolName(), reason);
    }
}

注意:若服务端仍无响应,重连后会再次死等——本步骤只能争取时间,不能替代根因修复。

<!-- 旧:mysql-connector-java 5.1.47 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>   <!-- 修复握手阶段 socketTimeout 失效 -->
</dependency>

<!-- 更推荐:迁移至 8.x,包名 com.mysql.cj.* -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.4.0</version>
</dependency>

若选择 5.1.49,JDBC URL 追加:

?connectTimeout=3000&socketTimeout=20000&tcpKeepAlive=true&tcpKeepAliveTime=30

注意:5.1.49 仅支持 JDK 8 / 11;JDK 25 必须迁移至 8.x。

hikari:
  connection-timeout: 5000          # 业务 getConnection 超时
  validation-timeout: 2000          # housekeeper 探测
  initialization-fail-timeout: 1    # 启动期快速失败
  keepalive-time: 30000             # 30s 心跳
  max-lifetime: 1800000             # 30min 驱逐
  minimum-idle: 5                   # 保持最低水位

提醒:这些参数无法解决"新建连接握手死等",但能让失效的存量连接更快被发现。

@Scheduled(fixedDelay = 30_000)
public void hikariWatchdog() {
    hikariPools.forEach(pool -> {
        var mx = pool.getHikariPoolMXBean();
        if (mx.getTotalConnections() == 0
                && mx.getIdleConnections() == 0
                && mx.getThreadsAwaitingConnection() > 0) {
            // 池已被打挂:驱逐卡死连接,触发重建
            mx.softEvictConnections();
            alert("Hikari pool " + pool.getPoolName() + " stuck, evicted");
        }
    });
}

软驱逐会触发下一次 fill 提交新 FutureTask;前提是服务端已恢复。

  • 开启 skip-name-resolve
  • 调高 max_connections / back_log,并合理设置 wait_timeout / interactive_timeout
  • 主从切换接入 ProxySQL / MHA 自动重试
  • 关键池接入 VIP 健康检查与漂移告警

  1. connectionTimeout 能管所有阻塞”——不能。该参数无法触及驱动内部 socket read。
  2. “HikariCP 4.x 已修复该 bug”——部分。async-init 仅切出"握手后"逻辑,握手 read 仍阻塞 adder。
  3. socketTimeout 调大就行”——5.1.47 握手阶段根本未应用该参数,必须升级驱动。
  4. “加 tcpKeepAlive=true 即可”——默认探测周期 2 小时,故障窗口内无法生效。
维度建议
JDK8 / 11 / 17 推荐;25 及以上需等待驱动官方认证
HikariCP≥ 4.0.3
MySQL 驱动≥ 5.1.49 或 8.x;5.1.47 及更早必须升级
监控HikariPoolMXBean 全量指标(active / idle / total / waiting / connect / lastAcquire)
SQLTransientConnectionException
    │
    ├── 抓 stack dump(间隔 ≥10 分钟两次)
    │      │
    │      ├── adder 线程栈底在 socketRead0 + 两次栈一致
    │      │      │
    │      │      ├── 驱动版本 < 5.1.49 且数据库为 MySQL → 升级驱动
    │      │      └── 抓包确认握手 read 是否有服务端回包
    │      │             │
    │      │             ├── 有回包 → 驱动 bug(升级驱动)
    │      │             └── 无回包 → 服务端问题
    │      │                   │
    │      │                   ├── max_connections / Threads_connected
    │      │                   ├── skip-name-resolve
    │      │                   ├── 主从切换 / VIP 漂移
    │      │                   └── 磁盘 IO / 防火墙
    │      │
    │      └── adder 线程在等锁 / 等 FutureTask → 池满或业务并发高
    │
    └── 观察 HikariPoolMXBean:active / idle / total / waiting 时间线

排查手册讲到这里,方法论已经齐了。下面两节是这次故障的原始证据——一份故障复盘如果只讲方法、不放原始材料,读者很难判断方法的可靠性。


2026-08-11 20:50:13
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.202-b08 mixed mode):

"MAIN_HikariCP connection adder" #8230979 daemon ... waiting on condition
   TIMED_WAITING (parking) - LinkedBlockingQueue.poll

"jdbc:mysql://10.14.127.31:19030/pool-A?... connection adder" #7952751 daemon ... runnable
   at java.net.SocketInputStream.socketRead0(Native Method)
   ...
   at com.mysql.jdbc.ConnectionImpl.loadServerVariables(ConnectionImpl.java:3797)
   ...
   at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:726)
2026-08-11 21:04:39
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.202-b08 mixed mode):

(同一组 tid #7952751/#7952621/#7952611/#7952602/#7952551/#7952367/
 #7952361/#7952206/#7952202/#7952145,栈帧完全一致)
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>4.0.3</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

故障现场已经摆出来了,接下来是这次分享里我最想留下的部分——13 个常见的追问。这一节源自故障复盘后两次内部 tech review 的 QA,我尽量保留了当时被问到的原话和回答。

A:在 5.1.47 上基本无效。握手阶段 MysqlIO.readFully → ReadAheadInputStream.fill 走的是内部 buffered stream,驱动并未将 SO_RCVTIMEO 应用到底层 socket,read 无限阻塞。必须升级驱动。

A:async-init 仅切出"握手后"的 connection-init-sql,握手 read 仍在 newConnection() 内同步执行,adder 仍被阻塞。4.x 无法覆盖该场景。

A:能重启 adder,但前提是服务端已恢复。服务端无响应时驱逐后重连仍会死等。

A:可以,但需注意:

  • 包名 com.mysql.jdbc.*com.mysql.cj.*
  • 默认时区 / SSL 行为有变化
  • 部分历史参数不再支持(如 useOldUTF8Behavior
  • 强烈建议灰度验证

A:可以,但客户端脆弱性依然存在——下次服务端再抖动还会卡住。建议客户端与服务端协同修复。

A:Druid 等主流连接池的 createConnection 同样同步等待驱动返回,根因同样存在。换连接池无法消除驱动握手 read 死等,反而会失去 HikariCP 的监控能力。

A:5.1.47 握手 read 链路为 MysqlIO.readFully → ReadAheadInputStream.read → ReadAheadInputStream.fill → underlyingInputStream.read → Socket.getInputStream().read → socketRead0。Socket 层 SO_RCVTIMEO 理论上可中断 read,但存在三个致命阻断:

  1. JDBC URL 上的 socketTimeout 在连接建立后才生效,应用层无法在握手 read 之前调用 setSoTimeout
  2. 握手阶段的内部 StatementImpl.executeQuery 走 internal statement,无 queryTimeout 路径,驱动也不会临时切换 SO_RCVTIMEO
  3. ReadAheadInputStream.readFromUnderlyingStreamIfNecessaryn=0 时无条件重试,不抛出任何超时、中断异常,阻塞无上限。

简言之:驱动在握手 read 前并未调用 setSoTimeout,即便 socket 支持该能力也无法触发。connectTimeout 控制的是 TCP 三次握手,不影响握手 SQL 的读包;tcpKeepAlive 默认关闭;Statement.setQueryTimeout 不覆盖驱动内部握手 read。

5.1.49 之后才在握手 read 前后显式保存并恢复 socket 超时,并以 connectTimeout 作为握手 read 的兜底超时;8.x 重写协议层,所有 read 统一受 socketTimeout 管控。

A:5.1.49 是 5.1 分支的最后一个 GA 版本(2022-07-25 发布,2023-10-17 进入 EOL,2024-04-30 起全面退役),累积了一批与本次故障直接相关的修复:

  1. 握手阶段 read 套用 connectTimeout 作为兜底超时——握手 SQL(loadServerVariablessetupServerForTruncationChecks)的 read 会被中断,抛出 CommunicationsException,adder 释放,池不再被打挂。
  2. tcpKeepAlive 默认开启,并新增 tcpKeepAliveTime / tcpKeepAliveInterval / tcpKeepAliveProbes 参数,可控内核 keepalive 探测间隔(不再依赖系统默认 7200 秒)。
  3. 握手失败路径主动关闭 socket——避免 5.1.47 时代"半死 fd"残留在池内。
  4. 失败时诊断信息更详细——CommunicationsException.getMessage() 输出 connectTimeout / socketTimeout / 远端地址等。
  5. TLS 1.3 支持MySQL 8.0 caching_sha2_password 兼容性改善utf8mb4_0900_ci 支持完善。

但 5.1.49 救不了"5.1.x 系列对 JDK 17 / 21 / 25 缺乏官方认证"这一根本问题。JDK 25 必须直接迁移至 8.x

A:5.1.x 最后一个版本 5.1.53 发布时 JDK 25 尚未出现,Socket 层在 JDK 17+ 的 NIO 行为变化(NioSocketImplEPollArrayWrapper 优化等)它并未适配。本次故障正是这一未认证矩阵的直接后果。8.x(com.mysql.cj.*)对 JDK 17+ 提供完整官方认证,HikariCP 4.x async-init + 8.x 协议层重写叠加,才构成 JDK 25 的稳态组合。

A:8.x 将握手阶段的 SQL 探测设计为可配置、可跳过。jdbcCompliantTruncation=true 在 8.x 下走异步协议层 read,受 socketTimeout 严格管控。即便服务端异常,socketTimeout 中断后会抛出 SQLException,adder 释放,不会再出现 5.1.47 时代的无限阻塞。

此外,8.x 内部将 loadServerVariables 这类握手探测改为懒加载:仅在真正使用变量时才查询,进一步收窄了握手 read 的窗口。

A:async-init 切出的是"握手后"的 connection-init-sql / setNetworkTimeout 等应用配置层逻辑,目的是避免 adder 被应用侧 init 脚本拖慢。但握手 read 仍发生在 Driver.connect 内部——即 newConnection() 的同步路径。4.x 将 init 切出后,adder 仍须等待 Driver.connect 返回才能进入下一步。驱动卡在握手 read 时,adder 永远等不到 Driver.connect 返回,因此 async-init 无法解决该问题。

驱动握手死等的真正解决方案是 5.1.49 的 connectTimeout 兜底配合 8.x 的协议层重写。

A:不能。Druid / Tomcat JDBC Pool / DBCP / Vibur 等所有主流连接池的"新建连接"均同步等待驱动返回。驱动卡在握手 read 上时,任何连接池的对应线程都会同步卡住。Druid 还自带心跳与后台校验逻辑,可能让故障更复杂。

换连接池无法消除驱动层根因。唯一能让池幸免的方式,是驱动自身能在 socketTimeout 内打断 read。

A:服务端修复是治本之策,与客户端升级同等重要。建议 DBA 同步处理:

  • skip-name-resolve=ON:避免握手阶段 DNS 反查拖慢连接
  • max_connections / back_log 留足 buffer
  • wait_timeout / interactive_timeout 与客户端 keepalive 探测周期对齐
  • 主从切换 / VIP 漂移时通过 ProxySQL / MHA 自动重试
  • 关键池接入 VIP 健康检查与漂移告警
  • 监控 unauthenticated user | login 状态的连接堆积
  • HikariCP#2161: Available connections in HikariPool occasionally drop to zero and not being released
  • HikariCP 4.0 release notes: async-init-sql
  • mysql-connector-java 5.1.49 changelog: socketTimeout / tcpKeepAlive 修复
  • MySQL 8.0 JDBC driver: connection handshake 重写

如果你的项目仍在使用 mysql-connector-java < 5.1.49,请把以下清单贴到下一个迭代里,按顺序执行:

  • 拉一份依赖清单,确认驱动版本;5.1.49 或 8.x 以下的,立即规划升级
  • JDK ≥ 17(含你这次在用的 JDK 25)的项目,直接迁移至 com.mysql.cj.* 8.x;JDK 8 / 11 可临时升 5.1.49 作为过渡
  • JDBC URL 追加 connectTimeout=3000&socketTimeout=20000&tcpKeepAlive=true
  • 上线 HikariPoolMXBean 全量指标(active / idle / total / waiting),告警阈值按 §3.1 的三项特征设置

如果已经在升级或排障过程中,欢迎把这次的 stack dump、JDBC URL、MySQL SHOW PROCESSLIST 输出贴到评论区,我帮你看看。

相关内容