连接池本质是资源复用,关键在取用、归还与超时淘汰;需协同调优客户端与服务端参数,避免泄漏、僵死连接及goroutine阻塞,非必要不自建。

连接池本质是资源复用,不是“越多越好”
Go 里没有内置的通用连接池,但 database/sql、net/http(底层 Transport)、Redis 客户端(如 go-redis)、gRPC 连接等都自带连接池。关键不是自己造轮子,而是理解它们怎么复用、何时释放、怎么避免泄漏。
核心复用逻辑:取用 + 归还 + 超时淘汰
一个健康的连接池靠三件事维持:
-
取用时优先复用空闲连接:比如 go-redis 的
GetWithContext会从 idle list 拿,没空闲才新建(受MaxActive或MaxOpenConnections限制) -
用完必须显式归还或自动回收:DB 操作后
rows.Close()、Redis 的conn.Close()(或用defer client.Close()不对,应 defer conn.Close());HTTP client 一般不用手动关,但 Response.Body 一定要Close() -
后台定期清理过期/失效连接:比如设置
IdleTimeout(空闲多久后关闭)、MaxLifetime(连接最大存活时间),防止僵死连接堆积
高并发下常见坑与应对
不是调大 MaxOpen 就万事大吉:
-
数据库连接数打满:PostgreSQL 默认 max_connections=100,若 Go 侧设
SetMaxOpenConns(200),实际只能建 100 个,其余阻塞或超时——要协同调优服务端和客户端参数 -
短连接误配长生命周期:比如
MaxLifetime=24h,但中间有 LB 断连或防火墙踢 idle 连接,导致后续请求报 “use of closed network connection”——建议设为 1~2 小时,并开启连接健康检查(如SetConnMaxIdleTime+TestOnBorrow类机制) - goroutine 泄漏隐性耗尽连接:比如 HTTP 请求未读完 Body、DB 查询未 Close Rows、Redis pipeline 发送后没接收响应——这些都会让连接卡在“忙”状态无法归还
自定义连接池只在必要时才写
除非对接私有协议或特殊中间件(如某定制 TCP 服务),否则别手撸连接池。真需要时,用 sync.Pool 管理轻量对象(如 buffer、request struct),用 container/list + mutex 或 channel 管理连接本身。重点控制:
立即学习“go语言免费学习笔记(深入)”;
- 创建连接的工厂函数(带重试和超时)
- 获取连接的阻塞策略(阻塞等待?返回 error?走 fallback?)
- 连接有效性校验(比如发个 PING 或简单 HEAD)
- 归还时区分“干净归还”和“标记损坏”(避免下次取到已断开的连接)
基本上就这些。连接池不复杂,但细节容易忽略,盯住“谁创建、谁释放、何时失效”这三件事,高并发下就稳得住。










