Skip to content

第32章:连接池与数据库配置优化 ​

本章目标 ​

学完本章后,你将能够:

  1. 理解 sql.DB 连接池的工作原理
  2. 根据业务需求配置连接池参数
  3. 监控连接池状态
  4. 排查连接泄漏问题

前置知识 ​

  • 需要先学习:第03章(连接数据库)
  • 需要了解:TCP 连接基本概念

32.1 连接池原理 ​

         ┌──────────┐
         │ Go App   │
         └────┬─────┘
              │
    ┌─────────▼─────────┐
    │    连接池 (sql.DB)  │
    │                    │
    │  ┌────┐ ┌────┐    │
    │  │空闲│ │空闲│    │  ← MaxIdleConns = 2
    │  └────┘ └────┘    │
    │  ┌────┐ ┌────┐    │
    │  │使用│ │使用│    │  ← 正在执行查询
    │  └────┘ └────┘    │
    │                    │  ← MaxOpenConns = 4
    └─────────┬──────────┘
              │
    ┌─────────▼──────────┐
    │    MySQL 服务器      │
    └────────────────────┘

32.2 四个核心参数 ​

go
sqlDB, _ := db.DB()

// 1. 最大打开连接数(默认:无限!)
sqlDB.SetMaxOpenConns(25)

// 2. 最大空闲连接数(默认:2)
sqlDB.SetMaxIdleConns(10)

// 3. 连接最大存活时间(默认:永久)
sqlDB.SetConnMaxLifetime(5 * time.Minute)

// 4. 空闲连接最大存活时间(Go 1.15+)
sqlDB.SetConnMaxIdleTime(1 * time.Minute)
参数默认值推荐值为什么
MaxOpenConns无限25-100防止连接数爆炸
MaxIdleConns2~10保留热连接,减少握手
ConnMaxLifetime永久< MySQL wait_timeout防止 MySQL 断开连接
ConnMaxIdleTime永久1-5 分钟及时释放空闲连接

32.3 连接池监控 ​

go
stats := sqlDB.Stats()

fmt.Printf("连接池状态:\n")
fmt.Printf("  最大打开: %d\n", stats.MaxOpenConnections)
fmt.Printf("  当前打开: %d\n", stats.OpenConnections)  // 已建立的连接
fmt.Printf("  使用中:   %d\n", stats.InUse)            // 正在执行查询
fmt.Printf("  空闲:     %d\n", stats.Idle)             // 空闲待命
fmt.Printf("  等待数:   %d\n", stats.WaitCount)        // 等待连接的请求数
fmt.Printf("  等待时长: %v\n", stats.WaitDuration)

32.4 连接泄漏排查 ​

连接泄漏是指连接被打开但没有正确释放:

go
// ❌ 连接泄漏
rows, _ := db.Raw("SELECT * FROM big_table").Rows()
for rows.Next() { ... }
// 忘记 rows.Close() → 连接永不归还

// ✅ 正确释放
rows, _ := db.Raw("SELECT * FROM big_table").Rows()
defer rows.Close()
for rows.Next() { ... }

32.5 实战:生产级连接池 ​

go
func InitDB(dsn string) (*gorm.DB, error) {
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		SkipDefaultTransaction: true,
		PrepareStmt:            true,
		Logger:                 logger.Default.LogMode(logger.Warn),
	})
	if err != nil {
		return nil, err
	}

	sqlDB, _ := db.DB()
	
	// 根据服务器规格配置
	sqlDB.SetMaxOpenConns(50)                  // 4核服务器推荐 25-50
	sqlDB.SetMaxIdleConns(15)                  // 约为 MaxOpenConns 的 1/3
	sqlDB.SetConnMaxLifetime(3 * time.Minute)  // 小于 MySQL wait_timeout(8h)
	sqlDB.SetConnMaxIdleTime(1 * time.Minute)  // 及时回收空闲

	return db, nil
}

本章小结 ​

  • 四个参数:MaxOpenConns、MaxIdleConns、ConnMaxLifetime、ConnMaxIdleTime
  • sqlDB.Stats() 监控连接池状态
  • 连接泄漏:忘记 rows.Close() 或 defer
  • 生产环境必须配置连接池上限

练习题 ​

  1. 配置一个连接池(MaxOpen=25, MaxIdle=10, MaxLifetime=5min),然后用 Stats() 监控状态。
  2. 写一段会泄漏连接的代码,然后用 Stats() 观察到连接数不下降。
  3. 修复上一题的连接泄漏。
  4. (思考题)为什么 MaxOpenConns 默认是"无限制"?这是设计缺陷还是有意为之?

把零散知识,整理成可以反复走的路。