gorm不提供自动重连机制,需通过类型断言提取驱动原生错误码(如postgresql的“08”前缀、mysql的2003/2013)识别连接断开,并结合连接池配置、主动ping探活与针对性重试策略实现高可用。

连接断开不是“重连一次就能好”的问题,GORM 本身不提供自动重连机制,底层复用 database/sql 连接池,真正要处理的是连接失效后的错误识别、恢复策略与连接池健康维持。
如何判断是连接断开而非其他 SQL 错误
不能只看 db.Error != nil 或 err != nil —— 这可能是语法错、约束冲突、超时等任意错误。必须深入错误类型,提取驱动原生错误码:
- PostgreSQL:用
errors.As(err, &pqErr)断言为*pq.Error,检查pqErr.Code[0:2] == "08"(如"08006") - MySQL:断言为
*mysql.MySQLError,检查myErr.Number() == 2003 || myErr.Number() == 2013(连接拒绝 / 连接丢失) - SQL Server:断言为
*mssql.Error,检查err.Number == 10060 || err.Number == 10061(连接超时 / 拒绝) - 通用兜底:若断言失败,且错误字符串含
"dial tcp"、"i/o timeout"、"connection refused",大概率是网络层断连
为什么调用 gorm.Open 重连是错的
gorm.Open 是初始化全局 *gorm.DB 实例的操作,它会新建连接池、加载配置、注册回调 —— 频繁调用会导致:
- 内存泄漏(旧连接池未 Close,新池不断创建)
- 连接数爆炸(每个
Open默认开启最大 100 连接) - 配置重复加载(如
PrepareStmt、日志级别被覆盖)
正确做法是复用已初始化的 *gorm.DB,仅对其底层 *sql.DB 调用 Ping() 检测,并依赖连接池自动重建空闲连接。
生产环境该怎么做:连接池 + 主动探活 + 错误重试
三项必须同时做,缺一不可:
- 配置合理连接池:
sqlDB.SetMaxIdleConns(10)、sqlDB.SetMaxOpenConns(50)、sqlDB.SetConnMaxLifetime(1h)(避免长连接僵死) - 启动后主动
Ping()一次,失败则 panic 或阻塞等待;运行中可定期(如每 30s)对sqlDB执行PingContext并记录日志 - 业务查询封装重试逻辑(非所有错误都重试):仅当
isConnectionError(err)返回 true 时,才在 100–500ms 后重试 1–2 次;其余错误(如主键冲突)立即返回
示例重试片段:
for i := 0; i <h3>SQL Server 密码含 <code>@</code> 时连接失败?那根本不是连接断开</h3><p>这是 DSN 解析失败,发生在 <code>gorm.Open</code> 阶段,压根没走到连接池初始化。错误表现是 <code>parse dns failed</code> 或 <code>invalid port</code> —— 因为 <code>@</code> 被误识别为 DSN 用户/密码分隔符或 host 分隔符。</p>
- 绝对不要拼接
sqlserver://user:pass@host:port这类 URL 形式 DSN - 改用参数化连接字符串:
server=host;user id=user;password=pass@123;port=1433;database=db - 确保传给
mssql.Open()的是完整字符串,且未被 URL 解码或截断
这类问题和连接断开无关,但常被误判 —— 它发生在应用启动期,而连接断开一定发生在 db.First、db.Create 等运行时调用中。











