Go 后端数据库连接池调优:连接数、慢查询与超时三件套
Go 后端数据库连接池调优:连接数、慢查询与超时三件套
先说结论
小服务器上的 Go 后端连数据库,最常见的死法不是 SQL 写错,而是连接池被打满:请求一多,goroutine 全堵在等连接上,接口超时、服务假死,重启才恢复。本文讲三个够用的调优动作:连接数按机器算清楚、慢查询先定位再动手、超时层层设防,三个做完,数据库就从“玄学瓶颈”变成“可观测的资源”。
第一步:连接数按机器算,不要拍脑袋
Go 的 database/sql 默认连接池上限是 0,也就是不限制,这在小服务器上等于埋雷。连接数要按两头算:数据库端能承受多少,应用端需要多少。
db.SetMaxOpenConns(20) // 同时打开的最大连接数
db.SetMaxIdleConns(5) // 空闲时保留的连接数
db.SetConnMaxLifetime(5 * time.Minute) // 单个连接最长存活时间
db.SetConnMaxIdleTime(1 * time.Minute) // 空闲连接多久后回收
算账的方法很实在:2 核 2G 的小服务器,数据库和应用跑在同一台机器上,MySQL 默认最多 151 个连接,但内存只够支撑几十个活跃连接;Go 后端把 MaxOpenConns 设到 20,留足余量给管理连接和突发。MaxIdleConns 设得比 MaxOpenConns 小很多,是为了让空闲连接及时回收,别占着数据库的内存。ConnMaxLifetime 一定要设,数据库和中间件(比如云厂商的代理)经常会在一段时间后静默断开旧连接,连接池里攒着一堆“僵尸连接”,请求拿到的第一件事就是报错重连。
验证方法很简单:SHOW PROCESSLIST 看看实际连接数是不是在预期内,压测时盯着 Threads_connected,如果连接数冲到上限还往上涨,说明池子太小或者有连接泄漏——查一下是不是有查询忘了 rows.Close()。
第二步:慢查询先定位,再动手优化
连接池打满,根因十有八九是慢查询:一条 3 秒的 SQL 占着连接不放,后面的请求全排队。先定位再优化,不要凭感觉加索引。
-- MySQL 先看慢查询日志有没有开,再看最耗时的几条
SHOW VARIABLES LIKE 'slow_query_log';
SELECT * FROM mysql.slow_log ORDER BY query_time DESC LIMIT 10;
定位到慢 SQL 后,按顺序排查三件事:第一,有没有命中索引,用 EXPLAIN 看 type 是不是 ALL(全表扫描),是就先补索引;第二,是不是 N+1 查询,循环里一次次查库的,改成一次 IN 查询批量取;第三,是不是在 SQL 里做了函数运算,比如 WHERE DATE(created_at) = ...,这种写法索引直接失效,改成范围条件。我的点餐小程序早期就踩过第二个坑:订单列表页先查订单再循环查每个订单的菜品,50 个订单就是 51 次查询,改成一次批量查询后接口耗时从 800ms 降到 60ms。
第三步:超时层层设防,别让一个慢查询拖垮全局
连接池和慢查询都调好了,还要防住极端情况:某个查询突然变慢,或者数据库短暂不可用,请求不能无限等下去。超时要分三层设:
// 第一层:请求级 context 超时,3 秒没结果就直接返回
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
defer cancel()
row := db.QueryRowContext(ctx, "SELECT ... WHERE id = ?", id)
// 第二层:驱动级超时,写在 DSN 里
// user:pass@tcp(127.0.0.1:3306)/db?timeout=2s&readTimeout=3s&writeTimeout=2s
// 第三层:连接池等待超时,排队等连接也别无限等
// database/sql 没有直接的等连接超时,靠第一层的 context 超时兜底
三层的意思是:任何一个环节卡住,最多 3 秒就释放资源、返回错误,而不是让 goroutine 堆积到服务崩溃。配合熔断效果更好:连续几次数据库超时后,先快速失败一部分请求,给数据库喘息的时间,恢复后再慢慢放行。
检查清单
- 连接数是不是按机器算过:
MaxOpenConns、MaxIdleConns、ConnMaxLifetime三个值都设了。 - 慢查询能不能定位:慢查询日志开着,
EXPLAIN会看,N+1已排查。 - 超时是不是分层设了:context 超时、驱动超时都有,单条慢 SQL 拖不垮全局。
- 连接有没有泄漏:
rows.Close()、defer都写对了,SHOW PROCESSLIST的连接数稳定。 - 有没有验证:压测时盯着连接数和慢查询日志,而不是凭感觉说“应该没问题”。
把这五条做完,数据库连接池就从“出事才想起调”的黑盒,变成“有数、有上限、有兜底”的可控资源。
- 点赞
- 收藏
- 关注作者
评论(0)