必须在dsn中显式配置connect_timeout参数:mysql用&connect_timeout=5(秒),postgresql用connect_timeout=5(秒整数),该值优先于gorm的connecttimeout配置,是阻断无效连接等待最前置、最可靠的控制点。

连接超时不是“等一会儿再报错”,而是必须在连接建立阶段就阻断无效等待,否则请求线程会卡死在 gorm.Open 或底层 sql.Open 调用里,拖垮整个服务。
DSN 里必须显式加 connect_timeout
MySQL 和 PostgreSQL 驱动都支持在 DSN 中直接指定连接级超时,这是最前置、最可靠的控制点。GORM 的 ConnectTimeout 配置只影响 gorm.Open 内部的 dial 阶段,但若驱动本身已通过 DSN 解析出更细粒度参数,它会优先使用 DSN 值。
- MySQL:在 DSN 后追加
&connect_timeout=5(单位秒),例如user:pass@tcp(127.0.0.1:3306)/db?connect_timeout=5 - PostgreSQL:用
connect_timeout=5(无&),例如host=localhost connect_timeout=5 user=pg dbname=test - 注意:PostgreSQL 的
connect_timeout是秒级整数,不支持小数;MySQL 支持5s或5000毫秒写法,但建议统一用秒
gorm.Config.ConnectTimeout 是兜底,不是主力
这个字段只在 DSN 未提供连接超时参数时生效,且仅作用于 gorm.Open 初始化那一刻的建连。它对后续连接池复用、空闲连接重连、或连接被数据库主动踢掉后的重建行为完全无效。
- 设了
ConnectTimeout: 30 * time.Second,但 DSN 里写了connect_timeout=3,最终生效的是 3 秒 - 该配置无法防止连接池中已有连接因网络中断变脏后,首次复用时卡住 —— 那属于“查询超时”范畴,得靠
Context控制 - 生产环境应始终以 DSN 参数为准,
ConnectTimeout只作为本地开发或测试时的 fallback
连接池空闲连接失效要靠 SetConnMaxLifetime
数据库服务端(如 MySQL 的 wait_timeout=28800)会在空闲连接超过阈值后单方面关闭它。GORM 连接池若继续复用该连接,就会抛 invalid connection。这不是超时问题,是连接老化问题,解决方式完全不同。
- 调用
db.DB().SetConnMaxLifetime(time.Hour * 4),确保该值比数据库wait_timeout小至少 10% - 不要设为
0(永不过期)或过大(如 24h),否则必然触发服务端强制断连 - 该设置只影响“连接从创建到被标记为可回收”的生命周期,不影响连接池大小或获取连接的等待时间
运行时连接失败必须靠错误码识别,不能只看 err != nil
GORM 不会把连接失败单独拎出来报错,它和语法错、约束冲突混在同一类 error 里。你得穿透 GORM 封装,拿到驱动原生错误,再按协议判断是否真连不上。
- PostgreSQL:断言为
*pq.Error,检查Code[0:2] == "08"(如"08006") - MySQL:断言为
*mysql.MySQLError,检查Number()是否为1040(Too many connections)、2003(Can't connect to MySQL server)等 - 别依赖
strings.Contains(err.Error(), "timeout")—— 网络超时、查询超时、锁等待超时都会带这个词,误判率极高
真正难处理的不是“连不上”,而是“以为连上了,其实连的是个半残连接”。这类连接能通过 ping,但执行第一条语句就卡住或返回诡异错误。唯一稳妥的做法,是在关键路径上做轻量探测查询(如 SELECT 1)并套上短超时 Context,而不是寄希望于某一个配置项一劳永逸。











