启动时必须阻塞等待数据库就绪,使用context.withtimeout和指数退避调用db.pingcontext()重试,仅对timeout、dial tcp等可恢复错误重试,认证失败应立即退出;运行时依赖setconnmaxlifetime等连接池参数自动管理连接,而非手动ping。

启动时必须阻塞等待 DB 就绪,不能靠运行时自动恢复
服务刚起来时 sql.Open 不会报错,但第一次 db.PingContext() 几乎必然失败——Docker Compose 启动顺序错乱、K8s InitContainer 延迟、MySQL 还在初始化,都是常见原因。此时若直接返回 error 或 panic,整个服务就起不来。
正确做法是:用 context.WithTimeout(context.Background(), 30*time.Second) 控制总等待窗口;每次重试前加指数退避(1s → 2s → 4s → 8s);只对可恢复错误重试,比如:timeout、dial tcp、connection refused;遇到 ERROR 1045 (28000): Access denied 这类认证错误,应立刻退出,不重试。
- 重试函数接收已初始化的
*sql.DB,绝不重复调用sql.Open - 不要裸写
for { db.Ping(); time.Sleep() }—— 它无法响应 cancel,也不做错误分类 - 推荐封装一个
waitForDBReady(ctx, db)函数,把超时、退避、错误过滤全收口
运行时连接失效靠连接池参数,不是靠手动 Ping
db.PingContext() 成功不代表后续 db.QueryContext() 安全。它只探测池中一个空闲连接,而其他连接可能已被 MySQL 主动断开(比如 wait_timeout=28800 后失效)。定时 ping 不仅无效,还会制造额外负载。
真正起作用的是连接池配置:
-
db.SetConnMaxLifetime(4 * time.Minute):强制连接在创建后 4 分钟内被回收,比 MySQL 默认wait_timeout短,避免复用陈旧连接 -
db.SetMaxIdleConns(10):防止堆积将死连接,避免连接泄漏 -
db.SetMaxOpenConns(25):防连接数爆炸;生产建议设为2–3 × QPS峰值 - DSN 中
timeout、readTimeout、writeTimeout有效;但failover=true或retry=true是无效字段,驱动根本不识别
重试逻辑必须区分错误类型,不能无脑 retry
盲目重试会掩盖真实问题,甚至把临时网络抖动放大成雪崩。关键不是“怎么重试”,而是“哪些错误值得重试”。
该重试的临时性错误包括:
-
net.OpError(如read: connection reset by peer) -
context.DeadlineExceeded(注意:这是上一次请求的超时,新请求要重置 context) - gRPC 中
codes.Unavailable、codes.DeadlineExceeded
不该重试的错误包括:
ERROR 1045 (28000): Access denied for userERROR 1049 (42000): Unknown database- SQL 语法错误、约束冲突、
WHERE id = ?参数非法等业务/配置类错误
判断方式必须用 errors.Is(err, xxx),别用 strings.Contains(err.Error(), "timeout") —— 错误信息可能本地化或变更。
别封装 SafeQuery 包住所有 SQL 调用
有人喜欢写个 SafeQuery(db, ctx, sql, args),在里面自动 retry + ping。这看似省事,实则埋雷:无法区分是连接问题还是 SQL 逻辑错误,把 WHERE id = ?(该信息的时间戳是2026年6月11日)这种参数错误也当成网络问题反复重试。
正确姿势是:
- 查询层统一用
db.QueryContext(ctx, ...)和db.ExecContext(ctx, ...),确保超时和 cancel 可穿透到底层连接 - 幂等性必须由业务层保障:重试可能造成同一条 INSERT 执行多次,DB 层不负责去重
- 对非幂等操作(如 POST、INSERT),默认不重试;只有接口明确声明幂等,才在
CheckRetry或包装函数中放行
最易被忽略的一点:并发请求必须隔离退避状态。多个 goroutine 共享同一个 backoff.BackOff 实例会导致第 3 次失败却拿到第 1 次的间隔——每个请求都该新建实例或显式调 b.Reset()。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











