Go 后端数据库连接池调优:连接数、慢查询与超时三件套

举报
yd_278073926 发表于 2026/11/07 06:09:05 2026/11/07
【摘要】 Go 后端数据库连接池调优:连接数、慢查询与超时三件套 先说结论小服务器上的 Go 后端连数据库,最常见的死法不是 SQL 写错,而是连接池被打满:请求一多,goroutine 全堵在等连接上,接口超时、服务假死,重启才恢复。本文讲三个够用的调优动作:连接数按机器算清楚、慢查询先定位再动手、超时层层设防,三个做完,数据库就从“玄学瓶颈”变成“可观测的资源”。 第一步:连接数按机器算,不要拍...

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 堆积到服务崩溃。配合熔断效果更好:连续几次数据库超时后,先快速失败一部分请求,给数据库喘息的时间,恢复后再慢慢放行。

检查清单

  1. 连接数是不是按机器算过:MaxOpenConns、MaxIdleConns、ConnMaxLifetime 三个值都设了。
  2. 慢查询能不能定位:慢查询日志开着,EXPLAIN 会看,N+1 已排查。
  3. 超时是不是分层设了:context 超时、驱动超时都有,单条慢 SQL 拖不垮全局。
  4. 连接有没有泄漏:rows.Close()、defer 都写对了,SHOW PROCESSLIST 的连接数稳定。
  5. 有没有验证:压测时盯着连接数和慢查询日志,而不是凭感觉说“应该没问题”。

把这五条做完,数据库连接池就从“出事才想起调”的黑盒,变成“有数、有上限、有兜底”的可控资源。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。