多个gorm.Open实例必须独立管理连接池,每个数据源需单独调用gorm.Open生成独立*gres.DB实例,并分别配置DSN、连接池、命名策略和时区参数,事务与AutoMigrate均不跨库生效。

多个gorm.Open实例必须独立管理连接池
Go 应用里开多个数据库连接,不是“复用一个 gorm.DB 改配置”就能切库的——gorm.DB 是不可变的封装,底层 *sql.DB 一旦创建就绑定固定 DSN。常见错误是试图在运行时修改已初始化的 gorm.DB 的 DSN 或驱动参数,结果完全无效,甚至引发 panic。
- 每个数据源必须调用一次
gorm.Open,生成独立的*gorm.DB实例 -
SetMaxOpenConns、SetMaxIdleConns等连接池设置,必须在gorm.Open后立刻从*gorm.DB获取*sql.DB再调用,例如:db1.Session(&gorm.Session{}).DB().SetMaxOpenConns(20) - 别把多个
*gorm.DB存进同一个全局变量,容易混淆;建议用 map 或结构体字段按用途区分,比如type DBs struct { Primary *gorm.DB, Log *gorm.DB }
gorm.Config 中的 NamingStrategy 必须按数据源单独配置
不同库可能用不同命名规范:主库用下划线(user_profile),日志库用驼峰(UserAction),或某些库表名全大写。GORM 的 NamingStrategy 不支持运行时切换,必须在每个 gorm.Open 时显式传入。
- 若主库需复用现有表结构,设
SingularTable: true并指定TablePrefix;日志库若用标准复数命名,就保持默认 -
TableName()方法仍优先于NamingStrategy,但注意:如果结构体同时定义了TableName()和gorm:"table_name:xxx"标签,后者会覆盖前者 - 跨库关联查询(如 join 主库用户表 + 日志库行为表)无法用 GORM 内置预加载,得手写
Raw或用Session分别查再内存合并
事务无法跨数据源,Transaction 只作用于单个 *gorm.DB
试图用一个 db.Transaction(func(tx *gorm.DB) error { ... }) 同时操作两个库,会报错或静默失败——GORM 的事务只绑定当前 *sql.Tx,而每个 *gorm.DB 对应独立的 *sql.DB。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 真要跨库一致性,只能上分布式事务(如 Seata),或退化为“尽力而为”:先提交主库,再异步发消息触发日志库写入,失败时靠补偿任务修复
- 同库内多表事务照常工作,但注意:如果用了
Session创建子*gorm.DB,它的事务上下文不继承父级,必须显式传tx - 测试时容易漏掉这个限制,本地单库跑通,上线双库后出现数据不一致
时区和字符集参数必须每个 DSN 单独控制
loc=Local、parseTime=true、charset=utf8mb4 这些不是 GORM 层的配置,而是 MySQL 驱动解析 DSN 时的行为。不同库可能部署在不同时区机器上,或要求不同字符集,不能共用一套 DSN 参数模板。
立即学习“go语言免费学习笔记(深入)”;
- 例如主库在 UTC+8 服务器,DSN 加
&loc=Asia%2FShanghai;审计库在 UTC 服务器,就得用&loc=UTC - 密码含
@或/必须 URL 编码,且编码后仍要保证整个 DSN 能被mysql.ParseDSN正确拆解,否则gorm.Open直接 panic -
parseTime=true缺失会导致time.Time字段读出来是字符串,写进去被转成0001-01-01,这种问题在线上查半天才发现是 DSN 漏了参数
*gorm.DB 实例,以及它们各自依赖的 DSN、连接池、命名策略和事务边界。最易被忽略的是:事务隔离范围、时区参数粒度、还有 AutoMigrate 只对单个实例生效——改了结构体,却只在主库跑了迁移,日志库表结构没更新,后续写入直接报 column not found。

















