golang 原生database\sql 的重连机制

15 阅读3分钟

数据库自动重连机制说明

Go 原生 database/sql 的重连机制

1. 连接池的自动管理

Go 的 database/sql确实有部分自动重连能力,但有限制:

✅ 支持的情况
  1. 连接失效自动重试

    • 如果驱动返回 ErrBadConndatabase/sql 会自动重试操作
    • 连接池会自动检测并移除失效的连接
    • 下次使用时,会从连接池获取新连接(如果连接池中有可用连接)
  2. 连接池自动补充

    • 当连接池中的连接失效时,会在需要时自动创建新连接
    • 只要 sql.DB 对象没有被关闭,连接池会持续工作
❌ 不支持的情况
  1. 整个连接池被关闭

    • 如果调用了 db.Close(),整个连接池被关闭
    • 不会自动重新打开,需要重新创建 sql.DB 对象
  2. 长时间停机后的恢复

    • 如果 MySQL 停机时间很长,连接池中的所有连接都可能失效
    • 虽然连接池会尝试创建新连接,但没有主动的健康检查
    • 只有在实际使用时才会发现连接失效并重试

2. 实际场景分析

场景1: MySQL 短暂停机(几秒到几分钟)
// 程序已连接
db, _ := sql.Open("mysql", "dsn...")

// MySQL 停机
// ... MySQL 重启 ...

// 下次使用时
rows, err := db.Query("SELECT * FROM users")
// ✅ 可能成功:如果连接池中有可用连接,会自动重试
// ⚠️ 可能失败:如果所有连接都失效,需要等待重试或手动处理

行为

  • database/sql 会检测到连接失效(返回 ErrBadConn
  • 自动重试操作,尝试从连接池获取新连接
  • 如果连接池中有可用连接,会成功
  • 如果所有连接都失效,会创建新连接
场景2: MySQL 长时间停机(几分钟到几小时)
// 程序已连接
db, _ := sql.Open("mysql", "dsn...")

// MySQL 长时间停机
// ... 所有连接都失效 ...

// MySQL 重启后
rows, err := db.Query("SELECT * FROM users")
// ⚠️ 第一次调用可能失败,需要重试
// ✅ 后续调用会成功(连接池已恢复)

行为

  • 连接池中的所有连接都已失效
  • 第一次调用时,会尝试使用失效的连接,失败
  • database/sql 检测到 ErrBadConn,自动重试
  • 重试时会创建新连接,成功
场景3: 调用 db.Close() 后
// 程序已连接
db, _ := sql.Open("mysql", "dsn...")

// 关闭连接池
db.Close()

// 尝试使用
rows, err := db.Query("SELECT * FROM users")
// ❌ 失败:sql: database is closed
// ❌ 不会自动重连,需要重新创建 sql.DB 对象

行为

  • sql.DB 对象被标记为已关闭
  • 所有操作都会返回 sql: database is closed 错误
  • 不会自动重新打开

实际使用建议

1. 对于 MySQL 短暂停机

原生机制通常足够

db, _ := sql.Open("mysql", "dsn...")
// 不需要特殊处理,database/sql 会自动处理连接失效

2. 对于 MySQL 长时间停机

建议添加重试逻辑

db, _ := sql.Open("mysql", "dsn...")

// 添加重试逻辑
var rows *sql.Rows
var err error
for i := 0; i < 3; i++ {
    rows, err = db.Query("SELECT * FROM users")
    if err == nil {
        break
    }
    time.Sleep(time.Second)
}

3. 对于需要 Close() 的场景

原生机制不支持

db.Close()
// ❌ 无法恢复,需要重新创建

总结

database/sql 原生机制

  • ✅ 处理连接失效和自动重试
  • ✅ 连接池自动管理
  • ❌ 不支持 Close() 后恢复
  • ❌ 没有主动健康检查