Golang中MySQL连接断开后如何实现自动重连
Golang中MySQL连接断开后如何实现自动重连 有没有办法检测到数据库连接丢失,然后重新连接?我需要这样做,因为我正在使用远程 MySQL 数据库…
谢谢。
2 回复
更多关于Golang中MySQL连接断开后如何实现自动重连的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Golang中实现MySQL连接的自动重连,可以通过以下几种方式:
1. 使用连接池的SetConnMaxLifetime和重连机制
package main
import (
"database/sql"
"fmt"
"log"
"time"
_ "github.com/go-sql-driver/mysql"
)
type DBManager struct {
dsn string
db *sql.DB
}
func NewDBManager(dsn string) *DBManager {
return &DBManager{dsn: dsn}
}
func (m *DBManager) Connect() error {
db, err := sql.Open("mysql", m.dsn)
if err != nil {
return err
}
// 设置连接池参数
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(25)
db.SetConnMaxLifetime(5 * time.Minute) // 连接最大生命周期
// 测试连接
if err := db.Ping(); err != nil {
return err
}
m.db = db
return nil
}
func (m *DBManager) GetDB() *sql.DB {
// 在执行查询前检查连接状态
if err := m.db.Ping(); err != nil {
log.Printf("连接断开,尝试重连: %v", err)
if reconnectErr := m.Connect(); reconnectErr != nil {
log.Printf("重连失败: %v", reconnectErr)
} else {
log.Println("重连成功")
}
}
return m.db
}
// 使用示例
func main() {
dsn := "user:password@tcp(remote-mysql:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
dbManager := NewDBManager(dsn)
if err := dbManager.Connect(); err != nil {
log.Fatal(err)
}
// 执行查询
var result string
err := dbManager.GetDB().QueryRow("SELECT 'test'").Scan(&result)
if err != nil {
log.Printf("查询失败: %v", err)
} else {
fmt.Printf("查询结果: %s\n", result)
}
}
2. 使用带重试机制的包装函数
package main
import (
"database/sql"
"log"
"time"
_ "github.com/go-sql-driver/mysql"
)
func WithRetry(db *sql.DB, maxRetries int, fn func(*sql.DB) error) error {
var lastErr error
for i := 0; i < maxRetries; i++ {
err := fn(db)
if err == nil {
return nil
}
lastErr = err
// 检查是否是连接错误
if isConnectionError(err) {
log.Printf("连接错误,尝试重连 (尝试 %d/%d)", i+1, maxRetries)
// 关闭旧连接
db.Close()
// 创建新连接
newDB, err := sql.Open("mysql", "user:password@tcp(remote-mysql:3306)/dbname")
if err != nil {
log.Printf("创建新连接失败: %v", err)
continue
}
// 更新引用
*db = *newDB
// 等待后重试
time.Sleep(time.Duration(i+1) * time.Second)
continue
}
// 非连接错误,直接返回
return err
}
return lastErr
}
func isConnectionError(err error) bool {
// 这里可以根据具体的错误类型判断
return err != nil && (err == sql.ErrConnDone || err.Error() == "driver: bad connection")
}
// 使用示例
func main() {
db, err := sql.Open("mysql", "user:password@tcp(remote-mysql:3306)/dbname")
if err != nil {
log.Fatal(err)
}
err = WithRetry(db, 3, func(db *sql.DB) error {
var count int
return db.QueryRow("SELECT COUNT(*) FROM users").Scan(&count)
})
if err != nil {
log.Printf("最终执行失败: %v", err)
}
}
3. 使用健康检查和定期重连
package main
import (
"database/sql"
"log"
"time"
_ "github.com/go-sql-driver/mysql"
)
type AutoReconnectDB struct {
dsn string
db *sql.DB
stopChan chan struct{}
}
func NewAutoReconnectDB(dsn string) *AutoReconnectDB {
return &AutoReconnectDB{
dsn: dsn,
stopChan: make(chan struct{}),
}
}
func (a *AutoReconnectDB) Start() error {
if err := a.connect(); err != nil {
return err
}
go a.healthCheck()
return nil
}
func (a *AutoReconnectDB) connect() error {
db, err := sql.Open("mysql", a.dsn)
if err != nil {
return err
}
if err := db.Ping(); err != nil {
return err
}
a.db = db
return nil
}
func (a *AutoReconnectDB) healthCheck() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if err := a.db.Ping(); err != nil {
log.Printf("健康检查失败,尝试重连: %v", err)
if reconnectErr := a.connect(); reconnectErr != nil {
log.Printf("重连失败: %v", reconnectErr)
} else {
log.Println("重连成功")
}
}
case <-a.stopChan:
return
}
}
}
func (a *AutoReconnectDB) GetDB() *sql.DB {
return a.db
}
func (a *AutoReconnectDB) Close() {
close(a.stopChan)
if a.db != nil {
a.db.Close()
}
}
// 使用示例
func main() {
dsn := "user:password@tcp(remote-mysql:3306)/dbname"
autoDB := NewAutoReconnectDB(dsn)
if err := autoDB.Start(); err != nil {
log.Fatal(err)
}
defer autoDB.Close()
// 正常使用数据库
db := autoDB.GetDB()
// ... 执行数据库操作
}
这些方法可以有效地检测和处理MySQL连接断开的情况,并自动重新建立连接。第一种方法在每次查询前检查连接状态,第二种方法在执行失败时重试,第三种方法通过定期健康检查来维持连接。

