gin项目需分离主从库dsn配置,主库用viper.get("mysql.master")单独初始化并设高连接池,从库独立配置(单台或多台),禁止共用host键或硬编码;读写路由须手动控制,事务、select for update及刚写入即查场景必须走主库,从库启用read_only=1防误写。

Gin项目里怎么配主库和从库的连接地址
不能只写一个dsn,得把主库、从库分开管理,否则读写分离无从谈起。Gin本身不内置读写分离逻辑,得靠你手动控制哪条SQL走哪个DB实例。
常见错误是把主从地址硬编码进database.go,或者用同一个*gorm.DB变量来回切换——这会导致连接复用混乱、事务跨库、从库被误写入。
- 主库连接单独初始化:用
viper.Get("mysql.master")读取独立配置段,创建*gorm.DB并设SetMaxOpenConns偏高(如100) - 从库连接可单个或多个:若只有一台从库,也单独初始化;若有多个,建议用
sql.Open原生连接池+负载均衡策略(如随机选一个),避免GORM自动重连时绑定到固定节点 - 配置文件中必须区分字段,例如
[mysql.master]和[mysql.slave],不要共用host键
如何在Gin Handler里决定走主库还是从库
最直接的方式是按HTTP方法或路由前缀判断,但更稳妥的是显式声明——别依赖“GET=读、POST=写”这种假设,因为有些GET接口会触发统计更新、缓存刷新等写操作。
推荐做法:封装一个DB()函数,接受write bool参数,内部返回对应连接;或用Context Value透传意图(比如c.Set("db_role", "slave")),再由中间件统一注入*gorm.DB。
- 事务必须强制走主库:
db.Transaction(func(tx *gorm.DB) error { ... })里的tx必须来自主库实例,否则报ERROR 1785(事务中不可跨库) - 带
SELECT ... FOR UPDATE或LOCK IN SHARE MODE的查询也必须走主库,从库默认read_only=1,执行会直接失败 - 刚写入就查(比如注册后立即查用户信息),需加
time.Sleep或重试逻辑,或直接查主库——别指望从库实时同步
CHANGE MASTER TO后从库没同步,Gin还连得上吗
能连上,但查不到新数据。Gin连接的是MySQL服务端口,只要TCP通、账号密码对、权限够,就能建连;复制是否工作,完全不影响应用层连接建立。
真正影响Gin行为的是SHOW SLAVE STATUS里的两个关键字段:Slave_IO_Running和Slave_SQL_Running。只要有一个是No,从库数据就滞后甚至停滞。
- 常见卡点:
Seconds_Behind_Master持续增长 → 检查从库磁盘IO、SQL线程是否被慢查询阻塞、主库binlog是否被清理(MASTER_LOG_FILE已不存在) -
Last_IO_Error提示Access denied→ 复制账号repl@'%'在主库没授权,或从库my.cnf里server-id和主库重复 -
Last_SQL_Error提示Duplicate entry→ 主从已有数据不一致,需停复制、手工修复、再SET GLOBAL sql_slave_skip_counter=1跳过(仅临时应急)
为什么用GTID模式比传统位点更适配Gin部署
因为Gin服务可能滚动发布、Pod重建、连接池重连,每次新建连接都可能面对不同从库实例。位点复制要求你精确维护MASTER_LOG_FILE和MASTER_LOG_POS,而GTID用MASTER_AUTO_POSITION=1自动定位,不怕日志轮转或从库重启丢位置。
但注意:GTID开启后,主从都必须配置gtid_mode=ON且enforce_gtid_consistency=ON,否则CHANGE MASTER TO会报错ER_GTID_MODE_NOT_SUPPORTED。
- 迁移旧系统时不能在线开启GTID:必须停写、
FLUSH LOGS、重启主从、再RESET MASTER,否则GTID集合冲突 - Gin调用
db.Exec("SET SESSION gtid_next='ANONYMOUS'")会破坏GTID一致性,禁止在事务中手动设置gtid_next - 从库开启
read_only=1后,Gin若误用UPDATE语句连从库,会直接报错ERROR 1290,这是好事——暴露了读写分离逻辑漏洞











