连接池满通常与fiber无关,而是因未设超时、并发失控或连接未释放所致;需合理配置maxopenconns/maxidleconns,监控openconnections与reserved指标,避免泄漏。

为什么连接池满不是 Fiber 的锅
连接池打满,90% 情况下跟 Fiber 本身无关——它只是在等数据库返回。真正卡住的是你没设超时、没控并发、或没释放连接的业务逻辑。Fiber 的协程调度快,但挡不住一个没 context.WithTimeout 的 db.Query 挂死在那里。
ConnectionPool.size 必须匹配实际并发压力
比如压测看到 5000 QPS,平均查询耗时 100ms,理论最小连接数 ≈ 5000 × 0.1 = 500。但实际要预留缓冲,建议设为 600–800;若用 sql.DB,则需调 SetMaxOpenConns(800) 和 SetMaxIdleConns(200)。
- 只设
MaxOpenConns不设MaxIdleConns:空闲连接不回收,内存涨,连接复用率低 - 设得过大(如 2000)但 DB 侧 max_connections=300:后端直接拒绝新连接,报错
ERROR: sorry, too many clients already - PHP 的
EventMachine::Synchrony::ConnectionPool同理,size: 4在高并发下就是瓶颈,不是“够用”而是“刚够测试”
别在 Fiber handler 里裸调 db.Query
同步阻塞调用 + 无超时 = 连接被长期占用。哪怕只慢一次,P99 延迟就跳变,连接池迅速积压。
- Go 示例:必须用
ctx, cancel := context.WithTimeout(c.Context(), 3*time.Second)包裹查询,查完立刻cancel() - PHP 示例:用
EM-Synchrony的aquery()而非query(),前者自动释放连接,后者可能阻塞 Fiber 调度器 - 所有中间件(如日志、鉴权)里涉及 DB 操作,也得套超时——别以为“只是查个用户信息就没事”
连接泄漏比连接池小更致命
最隐蔽的问题是:连接没被归还。常见于 panic 未 recover、defer 写错位置、或 channel 阻塞导致 defer db.Close() 永远不执行。
- Go 中检查泄漏:定期打印
db.Stats().OpenConnections,持续上涨即泄漏 - PHP 中监控
db.pool_status,若reserved长期 >available且不回落,说明有 Fiber 持有连接未释放 - 加
defer不等于安全——它只在函数 return 或 panic 时触发;若协程被 context 取消但没显式退出,defer 不会跑
reserved 和 OpenConnections 这两个指标,比反复调大 size 更有效。











