Go连接池必须配置SetMaxOpenConns≤数据库max_connections×0.8、SetMaxIdleConns为其1/2~2/3、SetConnMaxLifetime设为30s~5分钟以主动轮换连接,并立即调用db.Ping()验证连通性。

启动时必须阻塞重试,不能靠运行时自动恢复
服务刚起来时数据库还没 ready(比如 Docker Compose 依赖顺序错乱、K8s InitContainer 延迟),sql.Open 不会失败,但第一次 db.PingContext() 必然报错。此时若直接 panic 或返回 error,整个服务就起不来。
- 用
context.WithTimeout(context.Background(), 30*time.Second)控制总等待窗口 - 每次重试前加指数退避:
1 * time.Second → 2 * time.Second → 4 * time.Second,避免打爆 DB 健康检查端点 - 只对可恢复错误重试:
"dial tcp"、"connection refused"、io: timeout;遇到"password authentication failed"这类认证错误应立刻退出 - 重试函数接收已初始化的
*sql.DB,绝不重复调用sql.Open
SetConnMaxLifetime 是防 stale 连接的核心配置
db.PingContext 成功 ≠ 后续 Query 安全。MySQL 默认 wait_timeout=28800(8 小时),但生产常设为 300 秒(5 分钟)。若 Go 连接池不设 SetConnMaxLifetime,或设得 ≥ 300 秒,连接就会在服务端关闭后继续被复用,首次 Query 就失败。
- 推荐值:
db.SetConnMaxLifetime(240 * time.Second),留出 60 秒缓冲 - 设为 0 表示永不过期 → 放弃预防,全靠出错重试兜底,风险极高
- 该设置对所有连接统一生效,与是否 idle 无关;PostgreSQL 用户还需同步调
tcp_keepalives_idle等参数
运行时查询失败必须自己控制幂等重试
*sql.DB 内置重试仅在 driver.ErrBadConn 下触发,且最多硬编码 10 次,对网络抖动、主从切换、DNS 变更完全无效。你得自己写逻辑,但不能无脑重放写操作。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 只对
SELECT、GET类幂等操作重试;INSERT/UPDATE/DELETE需结合业务idempotency key或事务状态校验 - 用
context.WithTimeout包裹整个重试流程,总时间建议 ≤ 2 秒 - 退避推荐指数增长:
100ms → 200ms → 400ms… - 只捕获明确错误:
driver.ErrBadConn、net.OpError、"connection refused"、"timeout";认证失败、SQL 语法错不重试
别封装 SafeQuery 自动重试所有 SQL 调用
有人喜欢写个 SafeQuery(db, ctx, sql, args) 函数,在里面自动 retry + ping。这看似省事,实则埋雷:
立即学习“go语言免费学习笔记(深入)”;
- 无法区分是连接问题还是 SQL 逻辑错误,把
WHERE id = ?写成WHERE id == ?这类语法错也重试,掩盖真实问题 - 重试可能造成同一条
INSERT执行多次,DB 层不负责去重,幂等性必须由业务层保障 - 手动 Ping 不仅无效,还会制造额外负载;它不刷新连接池里其他连接的状态,也不触发重连逻辑
连接池生命周期管理比“重连”本身更关键——多数失效不是因为断开,而是连接在池中活得太久,被服务端静默 kill 了。SetConnMaxLifetime 和错误分类才是你最该盯住的两个点。

















