db.pingcontext不能替代健康检查,仅验证连接池中至少一个连接可用,不保证后续query成功;需配合setconnmaxlifetime(建议240秒)、幂等重试及合理连接池配置。

db.PingContext 不能当健康检查用
它只验证连接池里至少有一个连接能通,不保证后续 Query 能成功。高并发下,哪怕 db.PingContext 成功,db.Query 仍可能报 driver.ErrBadConn 或 "invalid connection" —— 因为那个“可用连接”可能刚被 MySQL 服务端按 wait_timeout 关闭,而 Go 客户端还没感知到。
必须用 db.PingContext(ctx),且 ctx 要带超时(比如 2 秒),否则阻塞无界;别把它塞进 for 循环里“保活”,它不刷新其他连接状态,纯属误导。
SetConnMaxLifetime 必须比数据库 wait_timeout 小
MySQL 默认 wait_timeout = 28800(8 小时),但生产环境常设为 300 秒(5 分钟)。如果 Go 连接池没设 db.SetConnMaxLifetime,或设得 ≥ 300 秒,连接就会在服务端关闭后继续被复用,首次 Query 就失败。
-
db.SetConnMaxLifetime(240 * time.Second)是稳妥值,留出 60 秒缓冲 - 设为 0 表示永不过期 → 直接放弃预防,靠出错重试兜底,风险极高
- 该设置对所有连接统一生效,和是否 idle 无关;PostgreSQL 用户还要同步调
tcp_keepalives_idle
Query 失败必须自己控制幂等重试
*sql.DB 内置重试只在 driver.ErrBadConn 下触发,最多 10 次硬编码,对网络抖动、主从切换、DNS 变更完全无效。
- 只对
SELECT、GET类幂等操作重试;INSERT/UPDATE/DELETE必须结合业务 idempotency key 或状态校验 - 用
context.WithTimeout包裹整个重试流程(总时间建议 ≤ 2 秒) - 退避推荐指数增长:100ms → 200ms → 400ms…
- 只捕获明确错误:
driver.ErrBadConn、net.OpError、"connection refused"、"timeout"
死锁 panic 日志是第一手线索,不是辅助信息
看到 fatal error: all goroutines are asleep - deadlock! 就该立刻停手——这不是卡顿,是程序彻底停摆。panic 输出的 goroutine 堆栈直接告诉你谁在等什么。
- 盯
maingoroutine 停在哪一行:比如卡在ch 、<code>mu.Lock()或wg.Wait() - 扫其他 goroutine 是否全卡在同一类操作上:如都在
chan receive,说明 sender 没人启动;都在(*RWMutex).RLock,说明读锁重入 - 有 goroutine 卡在
select {}或空for {}?那是忘了退出条件,不是资源争用,但也会触发死锁 - 日志被截断时,加
GODEBUG=schedtrace=1000 go run main.go,看 G 数量是否长期为 0
高负载下连接失效和死锁往往交织出现:连接池老化未清理 → Query 频繁失败 → 重试逻辑没做幂等 → 并发写操作重复提交 → 加锁顺序混乱 → 最终触发 mutex 死锁。真正难排查的,是那个没写 defer mu.Unlock() 的分支,或者在 RLock() 后又启动 goroutine 调同一把锁的地方。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











