MySQL 8.0 默认认证插件 caching_sha2_password 与旧版 Go 驱动不兼容,导致 gorm.Open 连接失败;解决方法是在 DSN 中添加 allowNativePasswords=true 参数,或执行 ALTER USER 切换为 mysql_native_password 并 FLUSH PRIVILEGES。

MySQL 8.0 默认认证插件导致 gorm.Open 连接失败
Go 项目用 GORM 连 MySQL 8.0 时,十有八九卡在连接阶段,报错类似 dial tcp 127.0.0.1:3306: connect: connection refused 或更隐蔽的 plugin 'caching_sha2_password' cannot be loaded。这不是网络问题,而是 MySQL 8.0 默认用 caching_sha2_password 认证插件,而旧版 MySQL 驱动(如 go-sql-driver/mysql v1.4 之前)不支持。
解决办法是显式启用兼容模式:在 DSN 后加 &parseTime=true&loc=Local&multiStatements=true&allowNativePasswords=true,其中 allowNativePasswords=true 是关键——它让驱动降级使用 mysql_native_password 握手流程。
- 确保用的是
github.com/go-sql-driver/mysqlv1.5+(GORM v2 默认依赖) - DSN 示例:
user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true - 如果仍失败,临时在 MySQL 中执行:
ALTER USER 'your_user'@'%' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES;
GORM v2 初始化时必须传入 *sql.DB 而非 DSN 字符串
GORM v2 的 gorm.Open 不再直接接受 DSN 字符串,而是要求先用数据库驱动(如 mysql.Open)创建 *sql.DB 实例,再传给 GORM。跳过这步会编译报错或 panic。
正确写法:
立即学习“go语言免费学习笔记(深入)”;
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
dsn := "user:pass@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
-
mysql.Open(dsn)返回的是gorm.Dialector,不是*sql.DB;GORM 内部会用它初始化连接池 - 别手动调
sql.Open再塞进gorm.Open—— GORM v2 不接受*sql.DB作为第一个参数 - 若需复用已有
*sql.DB(比如共享连接池),用gorm.Open(mysql.New(mysql.Config{Conn: yourSQLDB}))
时间字段映射出错:MySQL 8.0 的 TIMESTAMP 行为变化影响 CreatedAt
MySQL 8.0 对 TIMESTAMP 默认行为收紧:当未显式指定 DEFAULT CURRENT_TIMESTAMP 时,NOT NULL 的 TIMESTAMP 字段插入 null 会报错。而 GORM 的 CreatedAt、UpdatedAt 默认生成 TIMESTAMP NOT NULL,但没带默认值,建表就可能失败。
两种解法:
- 在 struct tag 中强制指定 MySQL 类型和默认值:
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null;default:CURRENT_TIMESTAMP"` - 全局配置 GORM 使用
Datetime类型(更稳妥):gorm.Config{NowFunc: func() time.Time { return time.Now().Local() }},并确保迁移时用db.Migrator().CreateTable(&User{})而非裸 SQL - 检查 MySQL 全局变量:
SELECT @@explicit_defaults_for_timestamp;,若为 OFF,建议升级到 ON(MySQL 8.0 默认 ON)以避免隐式行为
事务嵌套与 SavePoint 在 MySQL 8.0 下需手动管理
GORM 的 Session 和链式事务(如 db.Session(&session).Create())在 MySQL 8.0 下不会自动创建 savepoint。当你在事务中调用另一个也带事务逻辑的函数,容易因重复 COMMIT 或 ROLLBACK 导致状态混乱。
安全做法是显式用 SavePoint:
tx := db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.SavePoint("sp1").Create(&user).Error; err != nil {
tx.RollbackTo("sp1")
return
}
tx.Commit()
- MySQL 8.0 支持 savepoint,但 GORM 不自动包裹子逻辑,得自己调
SavePoint和RollbackTo - 避免在事务内调用其他封装了
db.Transaction的函数——它们会尝试新建事务,破坏当前上下文 - 用
db.Session(&gorm.Session{AllowGlobalUpdate: true})替代全局更新时,注意它不参与事务控制,慎用
MySQL 8.0 的权限模型、字符集默认值、JSON 字段解析都比 5.7 更严格,GORM 迁移或查询时只要一个字段类型没对齐,就可能静默失败或返回空结果。动手前先确认 SHOW VARIABLES LIKE 'character_set%' 和用户权限是否匹配应用预期。


















