正确连接 MySQL 需三步:1. DSN 含 parseTime=True&loc=Local&charset=utf8mb4;2. gorm.Open(mysql.Open(dsn), &gorm.Config{});3. 通过 db.DB() 设置连接池参数并调用 Ping() 验证。

直接用 gorm.Open 连 MySQL 很容易出错,不是连不上就是 panic,核心问题在于 DSN 格式、驱动初始化、连接池配置这三块没对齐。
DSN 字符串必须带 parseTime=True 和 loc=Local
MySQL 返回的时间字段(如 created_at)默认是 []uint8,GORM 不会自动转成 time.Time,除非你在 DSN 里显式声明时区和解析行为。漏掉这两个参数,Scan 或 Find 时大概率 panic:「cannot scan into *time.Time」。
-
parseTime=True告诉 MySQL 驱动把时间字段解析为 Go 的time.Time -
loc=Local避免时区错乱(比如存的是北京时间,查出来变成 UTC 时间) - charset 推荐固定用
utf8mb4,否则 emoji 或四字节字符会乱码 - 完整示例:
"root:pass@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"(注意 & 要转义为 &)
mysql.Open() 是函数,不是类型,别传错参数
常见错误是写成 gorm.Open(mysql.New(...), ...) 或 gorm.Open(&mysql.Config{...}, ...) —— 这两种都编译不过或运行 panic。GORM v2 要求第一个参数是 gorm.Dialector,而 mysql.Open(dsn) 才返回合法的 Dialector。
- ✅ 正确:
gorm.Open(mysql.Open(dsn), &gorm.Config{}) - ❌ 错误:
gorm.Open(mysql.New(...), ...)(mysql.New是 v1 写法,v2 已废弃) - ❌ 错误:
gorm.Open(&mysql.Config{DSN: dsn}, ...)(类型不匹配,*mysql.Config不是Dialector) - 如果要用自定义配置(比如禁用外键),得包在
mysql.Config{}里再传给mysql.Open,例如:mysql.Open(mysql.Config{DSN: dsn, DisableForeignKeyConstraintWhenMigrating: true})
连接池参数必须在 *sql.DB 层设置,不能只靠 GORM Config
gorm.Config 本身不控制连接池大小或生命周期,它只影响 GORM 行为(比如日志、事务、自动迁移)。真正管连接复用的是底层 *sql.DB 对象,必须从 DB.DB() 拿到后单独调用 SetMaxOpenConns 等方法。
- 必须做:
db, _ := gorm.Open(...);sqlDB, _ := db.DB();sqlDB.SetMaxOpenConns(100) - 不设
SetMaxIdleConns容易在压测时看到大量「dial tcp: lookup xxx: no such host」——其实是空闲连接被系统回收后,新请求来不及建连 -
SetConnMaxLifetime建议设为 300 秒(5 分钟),避免 MySQL server_timeout 导致连接僵死 - 别在每次请求里重新
Open,GORM 实例是线程安全的,全局复用一个*gorm.DB即可
viper 读配置时容易忽略的路径和类型问题
用 viper 加载 TOML/YAML 配置时,viper.ReadInConfig() 失败却不报具体原因,90% 是路径或类型没对上。
- 确保
viper.AddConfigPath("./config")指向的是二进制运行时所在目录的相对路径,不是 GOPATH 或 go.mod 目录 -
viper.SetConfigName("config")和viper.SetConfigType("toml")必须在ReadInConfig()之前调用 - 若配置文件叫
config.yaml,SetConfigType就得是"yaml",类型不匹配会静默失败 - 用
viper.Get("mysql.host")取值前,建议先viper.IsSet("mysql.host")判断是否存在,避免空字符串导致 DSN 拼接出错
最常被跳过的一步:没调 db.DB().Ping() 验证连接是否真通。开发时看着 gorm.Open 没 panic 就以为连上了,结果第一次查询才暴露 DNS 解析失败或权限拒绝。上线前加一行 if err := db.DB().Ping(); err != nil { log.Fatal(err) } 能省半天排查时间。


















