
本文详解如何使用 Go 的 database/sql 包安全、高效地连接多个 MySQL 数据库,涵盖连接初始化、连接池配置、读写分离实践及常见注意事项。
本文详解如何使用 go 的 `database/sql` 包安全、高效地连接多个 mysql 数据库,涵盖连接初始化、连接池配置、读写分离实践及常见注意事项。
在 Go 应用开发中,常需同时访问多个 MySQL 数据库(例如:分库分表、读写分离、多租户隔离或微服务间数据协同)。Go 原生 database/sql 包本身不绑定单一数据库实例,而是通过 sql.Open() 返回独立的 *sql.DB 句柄——每个句柄代表一个逻辑数据库连接池,彼此完全隔离。因此,连接多个 MySQL 数据库的核心原则是:*为每个目标数据库创建独立的 `sql.DB` 实例,并分别配置其连接参数与连接池行为**。
一、基础连接示例
以下是一个典型初始化三个 MySQL 数据库连接的函数:
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
// Connect 初始化并返回三个独立的数据库连接
func Connect() (*sql.DB, *sql.DB, *sql.DB, error) {
// 主库(写操作)
dbMaster, err := sql.Open("mysql", "user:pass@tcp(192.168.1.10:3306)/db_main")
if err != nil {
return nil, nil, nil, err
}
// 配置主库连接池(推荐)
dbMaster.SetMaxOpenConns(50)
dbMaster.SetMaxIdleConns(10)
dbMaster.SetConnMaxLifetime(30 * time.Minute)
// 从库1(读操作)
dbSlave1, err := sql.Open("mysql", "user:pass@tcp(192.168.1.11:3306)/db_replica1")
if err != nil {
return nil, nil, nil, err
}
dbSlave1.SetMaxOpenConns(30)
dbSlave1.SetMaxIdleConns(8)
// 从库2(读操作)
dbSlave2, err := sql.Open("mysql", "user:pass@tcp(192.168.1.12:3306)/db_replica2")
if err != nil {
return nil, nil, nil, err
}
dbSlave2.SetMaxOpenConns(30)
dbSlave2.SetMaxIdleConns(8)
return dbMaster, dbSlave1, dbSlave2, nil
}✅ 注意:
sql.Open()并不立即建立物理连接,仅验证 DSN 格式;首次执行Query/Exec等操作时才会触发连接建立(惰性连接)。务必调用Ping()进行显式健康检查(尤其在服务启动时):
if err := dbMaster.Ping(); err != nil {
log.Fatal("failed to ping master DB:", err)
}二、连接池与资源管理最佳实践
- 每个
*sql.DB实例维护独立的连接池,不可共享或复用; - 避免在短生命周期函数中反复
Open/Close——*sql.DB是长期存活、并发安全的句柄,应全局复用; - 在应用退出前,显式调用
db.Close()释放底层连接资源(非必需但推荐); - 合理设置
SetMaxOpenConns(避免过多连接压垮 MySQL)、SetMaxIdleConns(控制空闲连接数)和SetConnMaxLifetime(强制连接轮换,防止 stale connection)。
三、读写分离简易实现(无中间件场景)
可封装路由逻辑,将写操作导向 dbMaster,读操作按策略分发至 dbSlave1 或 dbSlave2:
type DBRouter struct {
Master *sql.DB
Slaves []*sql.DB // 支持 N 个从库
}
func (r *DBRouter) Write(query string, args ...any) (sql.Result, error) {
return r.Master.Exec(query, args...)
}
func (r *DBRouter) Read(query string, args ...any) (*sql.Rows, error) {
// 轮询选择从库(亦可基于负载、延迟等策略)
idx := atomic.AddUint64(&r.nextSlave, 1) % uint64(len(r.Slaves))
return r.Slaves[idx].Query(query, args...)
}总结
Go 中连接多个 MySQL 数据库本质是“多实例化”而非“多路复用”。关键在于:
? 为每个数据库创建专属 *sql.DB;
? 独立配置各连接池参数;
? 显式健康检查与统一生命周期管理;
? 结合业务需求设计读写路由逻辑(如需读写分离)。
只要遵循连接池复用、错误校验和资源释放规范,即可稳定支撑高并发多库访问场景。


















