1. 定位问题
登录数据库后执行:
SHOW PROCESSLIST;
找出 Sleep 状态连接,判断是否存在大量无操作但未关闭的连接。
按如下条件过滤:
Command = "Sleep"且Time > 300- 按
Host归类出哪些 Pod/服务泄漏连接
临时解决:强制关闭空闲连接:
sql
KILL <id>; -- 或者脚本批量 kill
2. 长久解决
你用的是 GoFrame 框架,建议检查你的连接池配置,特别是:
go
CopyEdit
// gcfg.yaml
database:
default:
link: "mysql:..."
maxIdle: 10 # 建议设置 # 最大空闲连接,防止闲置太多浪费连接
maxOpen: 50 # 连接数上限(需低于 mysql max_connections)
maxLifetime: "300s" # 避免长连接泄漏, 单个连接最大生存时间,避免长时间持有连接
- maxOpen 不要无限放大;
- maxLifetime 防止连接泄漏卡死;
- maxIdle 控制闲置连接数量,建议设置为比 maxOpen 小一截。
✅ 2. 是否有连接未释放?
GoFrame 使用 g.DB().Ctx(ctx).Model(...).All() 时,一般不会显式释放连接,因为它由连接池托管。但如果你用原始 driver/sql 查询、或未关闭 Rows,会造成泄漏!
例如这个就会泄漏:
go
rows, err := db.Query(...)
defer rows.Close() // ❗️必须调用
✅ 3. 慢查询 + 高并发 也会导致连接堆积
你提到的这条 SQL:
sql
SELECT ... FROM `case` WHERE status IN (1, 2) ORDER BY start_time ASC
是否涉及大表?慢查询?排序?分页?
可执行:
sql
EXPLAIN SELECT ...
如果扫描行数很大、没有索引或排序使用 filesort,很容易堆积连接。
🔧 建议:
- 确保
status、start_time有联合索引; - 分页查询(LIMIT 分段)而不是一次性查全。
✅ 4. 检查是否使用了事务但没有提交或回滚
go
tx, err := g.DB().Begin()
...
// ❗️必须最终 commit 或 rollback
事务未提交或回滚,连接会被占住,无法释放。
✅ 5. 观察慢查询 & 连接数走势
在 Azure MySQL:
- 打开慢查询日志(
slow_query_log=ON,long_query_time=1); - 使用 MySQL Insights 查看连接数变化;
- 设置
connection_threshold_alert告警。
✅ 如何缓解连接爆满问题?
| 方法 | 说明 |
|---|---|
| 降低 max_connections 设置压力阈值 | 避免服务无限制抢连接 |
| 配置合理连接池上限(maxOpen) | 不让 Go 应用吃光连接 |
| 开启连接复用、生命周期 | 避免连接泄漏或悬挂 |
| 慢查询优化 + 分页查询 | 减少每次请求占用时间 |
| 使用连接中间件(如 ProxySQL) | 控流或池化更高级别的连接管理 |
| 限流访问 | 拦截过多请求,保护数据库 |
Bonus: 多副本服务怎么安全配置连接池?
| 总连接上限 | 你的 Azure 配置(341) |
|---|---|
| 应用副本数 | 假设 10 个 |
| 每个实例最大连接数 | 341 / 10 = 34(推荐) |
可以把 maxOpen 设置为 30 左右,避免服务间相互抢连接。
默认值(GoFrame 使用 Go 的默认):
| 参数 | 默认值 | 说明 |
|---|---|---|
maxIdle | 2 | 最大空闲连接数(太小可能导致频繁创建销毁连接) |
maxLifetime | 0(永不过期) | 每个连接可用多久,0 表示连接永不关闭(⚠️可能导致连接泄漏) |
maxOpen | 无限(但 GoFrame 默认设为了 100) | 限制最大打开连接数,GoFrame 自己加了限制 |