GORM读写分离需显式配置主从连接池,不能拼接DSN;主库DSN传给gorm.Open初始化,从库DSN须通过dbresolver.Register().Sources/Replicas独立注册,事务内所有操作强制走主库,强一致读需手动指定Write策略,主从延迟由业务层兜底。

gorm.Open 时主从连接池必须显式分离,不能拼接 DSN
读写分离不是靠 GORM 自动识别 SQL 类型,而是靠你在 gorm.Open 阶段就明确声明主库(write)和从库(read)的独立连接池。常见错误是只传一个 DSN、或把多个地址用逗号拼成字符串(如 "user:pass@tcp(10.0.1.1:3306,10.0.1.2:3306)/db"),GORM 不识别这种写法,会报 invalid connection 或静默 fallback 到主库。
- 主库 DSN 单独传给
gorm.Open,用于初始化基础*gorm.DB,后续所有写操作和事务起点都依赖它 - 每个从库 DSN 必须调用
db.Use(dbresolver.Register()).Register(...)显式注册,sources放主库,replicas放从库列表(支持多个) - 从库 DSN 中的用户名/密码/地址/端口需与主库完全独立,尤其注意 MySQL 层是否已设
read_only=ON - 若某个从库地址不可达,
AddSlave或Register不报错,但后续读请求会 fallback 到主库 —— 日志无提示,得靠db.Debug().Find()观察实际执行 IP
事务内所有操作强制走主库,哪怕只做 SELECT
事务不是“只要没写就不走主库”,而是整个事务生命周期绑定单一连接。一旦调用 db.Begin(),得到的 *gorm.DB 或 *sql.Tx 实例的所有操作(包括 First()、Where().Scan())都会路由到主库。这是 ACID 隔离性的底线,不是可选策略。
- 错误写法:
tx, _ := masterDB.Begin()后又调slaveDB.QueryRow("SELECT ...")—— 这根本不是事务查询,是另一个独立会话,查不到刚写入的数据 - 正确做法:事务内所有操作必须基于
tx对象,比如tx.First(&u)、tx.Exec("UPDATE ...") - ORM 自动生成的语句(如 GORM 的
Preload关联查询)在事务中也自动走主库,无需额外干预 - 别在事务函数里无意识调用封装好的「读方法」,比如
ReadUserByID(id)内部用了slaveDB,会导致数据不一致
强一致读必须手动指定 Write 策略,不能依赖“刚写完就查”
主从延迟是 MySQL 复制机制决定的现实,GORM 不做任何延迟补偿。你看到“刚 Create 就 Find 不到”,大概率是业务没处理好读写时序,而不是配置错了。
- 需要强一致读(如支付扣款前查余额、创建后立即跳详情页),用
db.Clauses(dbresolver.Write).First(&u)或db.Session(&gorm.Session{AllowGlobalUnlock: true}).First(&u) -
Raw查询默认走从库,如果里面写了INSERT或SELECT FOR UPDATE,会直接报错ERROR 1290 (HY000): The MySQL server is running with the --read-only option - 避免在链式调用中间混入
Session,比如db.Where(...).Session(...).Select(...).Scan(),顺序错会导致策略失效 - 不要用正则匹配 SQL 字符串判断读写 ——
WITH RECURSIVE、INSERT ... SELECT等混合语句会误判,且 ORM 生成语句大小写、注释、嵌套复杂多变
从库连接池和健康检查必须独立管理
主库和从库不是“同一个 DB 换个地址”,而是负载特征完全不同的服务端点:主库写多、延迟敏感、连接数少;从库读多、可容忍延迟、连接复用率高。共用连接池参数会导致监控失真、熔断失效,甚至主库被慢查询拖垮。
立即学习“go语言免费学习笔记(深入)”;
- 每个从库实例必须单独调用
slaveDB.DB().SetMaxIdleConns(10)和SetMaxOpenConns(50),不能复用主库配置 - 必须设
SetConnMaxLifetime(10 * time.Minute),防止 MySQL 侧wait_timeoutkill 长连接后 Go 端仍尝试复用,引发大量重连 - GORM 不自动剔除宕机从库,
Replicas列表是静态配置;需配合外部探活(如 etcd 心跳)+Resolver.ReplaceReplicas()手动刷新 - 应用启动时从库不可达,
AddSlave返回error被忽略,后续读请求全打到主库 —— 务必检查每个注册操作的 error 返回
主从延迟无法由 ORM 消解,强一致性场景下,业务层必须主动控制读库选择;而连接池、健康检查、故障切换这些基础设施能力,GORM dbresolver 插件只提供钩子,不代劳实现。


















