Revel 框架本身不管理数据库连接池,真正由 database/sql 驱动和 GORM 负责;需在初始化 sql.DB 或 gorm.DB 后显式调用 SetMaxOpenConns、SetMaxIdleConns、SetConnMaxLifetime 才生效,且每个数据库实例须单独配置。

Revel 框架本身不直接管理数据库连接池,真正负责连接池的是底层的 database/sql(比如 mysql 或 postgres 驱动)和你选用的 ORM(如 GORM)。所以“在 Revel 中配置连接池”,本质是 在初始化 DB 实例时,正确设置 sql.DB 的连接池参数,并确保这些实例生命周期可控、不被意外复用或泄漏。
启动时调用 sql.DB.SetMaxOpenConns 等方法才生效
很多人以为在 sql.Open 的 DSN 里加 &maxOpen=20 就能生效——不行。MySQL 驱动(go-sql-driver/mysql)根本不识别这类参数;PostgreSQL 驱动(lib/pq 或 pgx)也不支持。连接池控制必须显式调用 SetMaxOpenConns、SetMaxIdleConns、SetConnMaxLifetime 这三个方法:
-
SetMaxOpenConns(20):硬性限制最大并发连接数,超出会阻塞等待(不是报错) -
SetMaxIdleConns(10):空闲连接最多保留几个,设太小会导致频繁建连,设太大可能占住数据库资源 -
SetConnMaxLifetime(60 * time.Second):连接复用上限时间,避免因数据库主动断连导致connection is already closed
这些必须在 sql.Open 返回 *sql.DB 后立刻设置,且只设一次。Revel 的 app.Init() 是最合适的调用位置。
用 GORM v2+ 时别绕过 gorm.Config 直接操作底层 sql.DB
如果你用的是 GORM(推荐 v2),它内部已封装了 sql.DB,但默认不会帮你调用上面那三个方法。你得在创建 *gorm.DB 时传入自定义 gorm.Config,并通过 gorm.Config.ConnPool 或初始化后手动获取底层 *sql.DB 来配置:
- 方式一(推荐):创建 GORM 实例后,立即拿到底层
*sql.DB并设置 - 方式二:用
gorm.Config.PrepareStmt和gorm.Config.SkipDefaultTransaction做轻量适配,但连接池仍需手动设 - 错误做法:在
app.conf里写db.maxopen=20—— Revel 不解析这种键,纯属无效配置
示例代码片段(放在 app/init.go):
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
sqlDB, err := db.DB()
if err != nil {
panic(err)
}
sqlDB.SetMaxOpenConns(20)
sqlDB.SetMaxIdleConns(5)
sqlDB.SetConnMaxLifetime(60 * time.Second)
多个数据库共存时,每个 *sql.DB 实例都要单独配池
Revel 支持多数据源(比如主从分离、分库分表),但每个 *sql.DB 实例都维护独立连接池。不能只配一个,也不能共享同一个 *sql.DB 给不同库用——GORM 的 Session 或 WithContext 切换的是查询上下文,不是连接池。
- 主库和从库必须各自初始化
*sql.DB,各自调用SetMaxOpenConns - 分片场景下(如按 user_id 路由到 shard_001/shard_002),每个分片 DB 实例也需单独配置,且建议根据分片负载差异差异化设置(比如热分片开 30 连接,冷分片开 5)
- 切记不要在 handler 里反复调用
sql.Open—— 这会绕过连接池,快速耗尽文件描述符
连接池没生效的典型症状和排查点
如果发现数据库响应变慢、偶发 sql: connection is already closed 或 dial tcp: lookup xxx: no such host,大概率是连接池没配对:
- 检查是否漏掉
SetConnMaxLifetime:MySQL 默认 wait_timeout=28800 秒,但很多云数据库(如阿里云 RDS)设为 300 秒,不设 lifetime 就容易复用已断连 - 检查
SetMaxOpenConns是否小于压测并发量:比如设了 10,但压测 QPS=50,就会大量阻塞在获取连接上 - 检查 Revel 的
Filter或中间件里有没有意外调用db.Close():Revel 的全局变量生命周期长,Close()会关掉整个池 - 用
sqlDB.Stats()打印实时状态(上线后慎用,有性能开销),确认OpenConnections和InUse是否符合预期
连接池参数不是设完就高枕无忧——它和你的业务并发模型、数据库侧 timeout 设置、网络稳定性强耦合。每次上线前,至少用 ab 或 hey 对单个接口压测 2 分钟,观察 sql.DB.Stats() 输出变化,才能确认真生效了。


















