不能靠一个sql.db变量硬切多个库,必须为每个数据源独立初始化sql.db实例,分别配置连接池参数,避免混用导致事务错乱、连接泄漏或panic;各实例需用独立变量(如userdb、orderdb),禁止全局复用或init()中隐式初始化,推荐显式工厂函数创建,并通过接口(如queryer)隔离驱动差异,租户场景宜用sync.map缓存按需加载。

不能靠一个 *sql.DB 变量硬切多个库,Go 原生不支持运行时切换底层 driver 或 DSN,强行复用会导致事务错乱、连接泄漏、甚至 panic。
每个数据源必须独立初始化 *sql.DB 实例
哪怕两个 MySQL 实例只差一个数据库名(如 user_center 和 order_service),也得各自调用 sql.Open,不能共用变量或全局单例。
-
userDB, err := sql.Open("mysql", dsn1)和orderDB, err := sql.Open("mysql", dsn2)必须是两个独立变量 - 每个实例都要单独配置连接池:
userDB.SetMaxOpenConns(20)、orderDB.SetMaxOpenConns(50),避免某库突发流量拖垮其他库 - 别在
func init()里初始化 DB:错误无法透出,启动失败难定位;改用显式函数如NewUserDB(cfg) (*sql.DB, error) - YAML 配置建议扁平化,例如:
mysql.user_db.dsn和mysql.order_db.dsn,而不是把 host/user/password 拆成顶层字段
用接口隔离驱动差异,别抽象 BeginTx 统一路由
PostgreSQL 的 pgx 和 MySQL 的 mysql 驱动返回的虽都是 *sql.DB,但事务行为、错误码、隔离级别支持完全不同,交叉使用必出问题。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 定义轻量接口暴露通用能力:
type Queryer interface { QueryRowContext(...); ExecContext(...) } - 模块只注入所需接口,比如日志模块只接收
Queryer,而非整个DBRouter结构体 - 别在封装层统一实现
BeginTx:MySQL 不支持SAVEPOINT在READ UNCOMMITTED下生效,PostgreSQL 对ISOLATION LEVEL解析更严格,强行抽象反而增加出错概率 - 事务跨库无效——
userDB.Transaction()内调用logDB.Create(),后者完全游离在事务外
按租户或请求上下文动态路由时,用 sync.Map 缓存 + 延迟初始化
不是所有场景都需要手动 if-else 切库。SaaS 多租户这类需求,应提前建好映射表,按需加载,而不是每次请求都重连。
- 用
var dbMap = sync.Map{}存tenantID → *sql.DB,key 是字符串,value 是已初始化好的*sql.DB - 首次访问某租户时才调用
sql.Open初始化对应实例,并设置连接池参数,避免启动时全量加载 - 中间件中从
r.Header.Get("X-Tenant-ID")提取标识,查主库确认有效性后,再从dbMap取实例,塞进context.WithValue - Handler 中通过
ctx.Value(tenantDBKey).(*sql.DB)获取连接,而不是根据条件判断该用哪个变量
GORM 多实例共存的三个关键雷区
GORM 的 *gorm.DB 是带状态的完整实例,每个都含独立连接池、回调链、日志器,多个之间零共享,但误操作会让它们互相污染。
- 驱动导入漏下划线:
import _ "gorm.io/driver/mysql"少那个_,gorm.Open不报错,第一次查询直接卡死或返回"invalid connection" - DSN 特殊字符未编码:PostgreSQL 用户名含
@或密码含/,必须用url.QueryEscape包一层,否则解析失败只报"pq: password authentication failed" - 事务跨库无效:GORM 做不了分布式事务,业务层得用补偿逻辑,而不是指望
Transaction()跨实例生效
最易被忽略的一点:连接泄漏往往不是没调 db.Close(),而是多个 *sql.DB 实例共用了同一个底层网络连接池(比如误传了相同 net.Conn),或者在 defer 中错用了非对应实例的 Close()。每个 *sql.DB 的生命周期必须独立管理。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










