gorm v2 连接 mysql 失败主因是驱动导入错误或 dsn 缺关键参数:必须 import _ "gorm.io/driver/mysql",dsn 含 user:pass@tcp(ip:port)/db?charset=utf8mb4&parsetime=true&loc=local。

gorm.Open 传参顺序和 DSN 格式必须严格匹配
连接 MySQL 失败最常见的原因是 gorm.Open 的第一个参数类型不对,或 DSN 字符串缺关键字段。GORM v2 不再接受 mysql 驱动的旧式参数(如 host、port 单独传),必须用完整 DSN 字符串。
正确写法是先导入驱动:import _ "gorm.io/driver/mysql",再调用:
db, err := gorm.Open(mysql.Open("user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"), &gorm.Config{})
-
user:password@tcp(127.0.0.1:3306)/dbname是必需骨架,tcp不能省略,IP 和端口要可连通 -
charset=utf8mb4必须显式指定,否则中文可能乱码或插入失败 -
parseTime=True才能让time.Time字段正常读写;不加会导致时间字段全为零值 -
loc=Local避免时区错位(尤其容器环境默认 UTC,但应用本地是 CST)
连接池配置必须在 gorm.Open 后立即设置
GORM 不在 gorm.Open 里处理连接池参数,而要通过 *gorm.DB 拿到底层 *sql.DB 实例后单独配置。漏掉这步,高并发下容易出现 dial tcp 127.0.0.1:3306: connect: cannot assign requested address 或超时。
实操代码:
sqlDB, err := db.DB()
if err != nil {
panic(err)
}
sqlDB.SetMaxOpenConns(100)
sqlDB.SetMaxIdleConns(20)
sqlDB.SetConnMaxLifetime(time.Hour)
-
SetMaxOpenConns控制最大连接数,建议设为应用 QPS × 平均查询耗时(秒)× 2,而非盲目调大 -
SetMaxIdleConns应 ≤SetMaxOpenConns,否则空闲连接堆积会占满数据库许可 -
SetConnMaxLifetime推荐设为 1 小时以内,避免 MySQL 的wait_timeout主动断连导致“invalid connection”错误
迁移失败时优先检查 gorm.Model 和表名映射
执行 db.AutoMigrate(&User{}) 报 Error 1046: No database selected 或字段没建上,大概率不是 DSN 问题,而是结构体没配好。
- 确保结构体字段是导出的(首字母大写),且有
gormtag,例如:ID uint `gorm:"primaryKey"` - 默认表名是复数小写(
users),如果想用User对应user表,得全局设置:gorm.NamingStrategy{SingularTable: true} - 若用
db.Table("my_user").AutoMigrate(&User{}),则AutoMigrate只作用于该表名,不会创建users - MySQL 8.0+ 默认启用
caching_sha2_password插件,旧版驱动可能不兼容,报client does not support authentication protocol—— 此时需在 MySQL 里执行:ALTER USER 'user'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
事务中嵌套调用必须传入 *gorm.DB 实例
写业务逻辑时容易把 db.Create() 直接写在事务块里,结果事务不生效:数据仍被提交,回滚无效。根本原因是 GORM 的事务对象是新生成的 *gorm.DB,和原始 db 不同。
正确方式是用 tx := db.Begin() 得到事务句柄,后续所有操作都基于 tx:
tx := db.Begin()
if err := tx.Create(&User{Name: "A"}).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&Order{UserID: 1}).Error; err != nil {
tx.Rollback()
return err
}
tx.Commit()
- 绝不能混用
db.Create和tx.Create,它们走的是不同连接 - 事务内调用其他函数时,必须把
tx当参数传进去,而不是在函数里重新用全局db - MySQL 默认隔离级别是
REPEATABLE READ,GORM 不自动设FOR UPDATE,需要显式写tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&u)才加行锁











