DSN字符串为空或格式错误是最常见的连接失败原因,如v.GetString("db.host")返回空导致生成":@tcp(:)/",底层net.Dial报"dial tcp: unknown port tcp/";必须检查user、password、host、port、dbname是否非空,并在gorm.Open前打印完整DSN。

DSN 字符串为空或格式错误
最常见的连接失败不是数据库挂了,而是 dsn 根本没拼对。比如 v.GetString("db.host") 返回空字符串,最终生成的 DSN 变成 :@tcp(:)/,底层 net.Dial 会报 dial tcp: unknown port tcp/——这不是连不上,是压根没东西可连。
- 检查所有 DSN 组成字段(
user、password、host、port、dbname)是否非空,建议在gorm.Open前加日志打印完整dsn - PostgreSQL 密码含
@或$时必须用url.QueryEscape()编码,否则解析失败 - MySQL 连接字符串中
parseTime=True和loc=Local要配对,否则 time.Time 字段可能扫描异常,间接导致连接后首次查询失败
连接建立阶段超时或被拒绝
gorm.Open 不拨号,真正建连发生在第一次 Ping()、Query() 或 Exec()。此时若网络不通、防火墙拦截、DNS 解析慢、MySQL max_connections 耗尽,就会卡住并超时。
- 现象是服务启动后首个请求延迟 2–5 秒,日志出现
dial tcp: i/o timeout或connection refused - 必须在
gorm.Open后立即调用db.WithContext(ctx).Ping(),且ctx要带超时(如 3–5 秒),避免阻塞整个启动流程 - K8s 环境下 readiness probe 失败常源于此,预热不做到位,pod 就永远进不了 Ready 状态
数据库权限或状态异常
连接字符串和网络都 OK,但依然报 invalid connection 或权限错误,大概率是 MySQL 用户没授够权,或数据库本身处于只读、连接数满、wait_timeout 触发连接失效等状态。
- 用命令行手动执行
mysql -h $HOST -P $PORT -u $USER -p验证账号密码和网络可达性 - 登录 MySQL 后执行
SHOW VARIABLES LIKE '%timeout%';查wait_timeout,GORM 底层连接池需设置SetConnMaxLifetime小于该值(如设为 4 小时,而 MySQL 是 8 小时) - 检查用户权限:
SELECT User, Host FROM mysql.user;和SHOW GRANTS FOR 'your_user'@'%';,确保有SELECT、INSERT等必要权限
AutoMigrate 无声失败误导判断
很多人以为 AutoMigrate 报错才说明连不上,其实它完全不校验连接有效性。即使 db 是 nil、用户无权限、表结构类型不兼容(如 MySQL 8 严格模式下 int64 对应 TINYINT),它也静默跳过,不建表也不报错。
- 务必先确认
db != nil且db.Migrator().CurrentDatabase()能返回数据库名,否则后续所有操作都不可信 - 模型 struct 必须含
gorm:"primaryKey"字段,否则 GORM 认为模型无效,直接忽略整张表 - 调试时可在
AutoMigrate后立刻查表:db.Table("users").Count(&n),看是否真能访问


















