GORM v2 的 dbresolver 插件需显式配置主从连接池并依赖操作语义路由,而非自动解析SQL;未正确注册插件或写反Sources/Replicas会导致INSERT打到从库报错,Create/Update/Delete默认走主库,Find/First默认走从库,事务内所有操作强制走主库,强一致读须手动标注Clauses(dbresolver.Write)或Session。

GORM v2 的 dbresolver 插件不是“自动识别 SQL 类型”然后分发,而是靠你显式配置主从连接池 + 依赖操作语义做路由。没配对或写错策略,INSERT 打到从库会直接报 ERROR: cannot execute INSERT in a read-only transaction。
gorm.Open 阶段必须注册 dbresolver 插件
只调用 gorm.Open 初始化一个 *gorm.DB 不足以启用读写分离——GORM v2 默认不带该能力,必须手动引入并注册插件。
- 导入插件:
import "gorm.io/plugin/dbresolver" - 主库先建好:
masterDB, err := gorm.Open(mysql.Open(dsnMaster), &gorm.Config{...}) - 再注册 resolver:
masterDB.Use(dbresolver.Register(dbresolver.Config{Sources: []gorm.Dialector{mysql.Open(dsnMaster)}, Replicas: []gorm.Dialector{mysql.Open(dsnSlave1), mysql.Open(dsnSlave2)}})) - 常见错误:漏掉
.Use(...),或把Sources和Replicas写反,导致写操作全走只读库
Create/Update/Delete 默认走主库,Find/First 默认走从库
路由规则由方法语义决定,不是 SQL 文本内容。GORM 不解析 SQL 字符串,只看调用链上是否含写操作意图。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
Create、Save、Update、Delete、Transaction→ 强制走Sources(主库) -
Find、First、Where().Scan()、Model().Count()→ 默认走Replicas(从库) -
Raw("SELECT ...")默认也走从库;但Raw("INSERT ...")会失败——必须显式加.Clauses(dbresolver.Write) - 事务内所有操作(包括
SELECT)都强制走主库,哪怕只是查刚写入的记录
强一致读必须手动切主库,不能依赖“刚写完就查”
MySQL 主从延迟是物理现实,GORM 不重试、不等待、不补偿。所谓“查不到最新数据”,99% 是业务没控制好时序。
- 刚
Create完立刻First,大概率读到旧数据——因为从库还没同步完 - 正确做法:
db.Clauses(dbresolver.Write).Where(...).First(&u)或db.Session(&gorm.Session{AllowGlobalUnlock: true}).First(&u) - 注意:
Session必须在链式调用最开头,Where().Session().First()无效 - 从库连接池建议设更高
MaxOpenConns(比如 200),主库保守些(比如 50),避免写操作被连接耗尽阻塞
真正难的不是配通读写分离,而是想清楚哪些读必须强一致、哪些可以接受延迟,并在代码里显式标注。GORM 只管分发,不管业务语义——延迟带来的数据不一致,得由你来兜底。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










