连接池耗尽是表象,长事务才是根因;需通过pg_stat_activity定位idle in transaction会话,强制终止阻塞进程并修复代码中事务未提交/回滚问题。

连接池耗尽是表象,长事务才是根因
看到 SQLTransientConnectionException: Connection is not available, request timed out after 30000ms 不要急着调大 maximumPoolSize。连接池 active=80、idle=0、pending 持续上涨,说明连接全被占住且无法释放——问题不在池子大小,而在“谁拿了不还”。最常见就是业务代码开启事务后,因 panic、异常分支、或忘记 tx.Commit()/tx.Rollback(),导致连接长期卡在 idle in transaction 状态。
PostgreSQL 快速定位长事务和锁等待
连上数据库直接跑这条 SQL,重点看 xact_duration 和 state:
SELECT pid, usename, application_name, state, now() - xact_start AS xact_duration, now() - query_start AS query_duration, wait_event_type, wait_event, query FROM pg_stat_activity WHERE state != 'idle' AND xact_start IS NOT NULL ORDER BY xact_start;
-
state = 'idle in transaction':事务已空闲但未提交/回滚,99% 是代码漏处理 -
xact_duration > '5 minutes':高危,快照膨胀、锁堆积、阻塞写入 -
wait_event_type = 'Lock':当前会话正在等锁,已被阻塞
GORM 事务中必须显式控制生命周期
GORM 的 db.Transaction() 只是语法糖,底层仍依赖你手动保证 commit/rollback。以下写法极危险:
tx := db.Begin()
// 忘记 defer tx.Rollback() 或 if err != nil { tx.Rollback() }
// 中间逻辑 panic → 事务不结束,连接卡死
tx.Commit()
正确姿势是:
- 用
defer func(){ if r := recover(); r != nil { tx.Rollback() } }()捕获 panic - 所有错误分支必须显式
tx.Rollback(),不能只靠 defer(defer 在函数返回时才执行,panic 后可能跳过) - 避免在事务内做 HTTP 调用、文件读写等不可控耗时操作
阻塞链路确认与紧急止损
如果已确认有 idle in transaction 卡住下游,别等自动超时。先查谁在堵:
SELECT blocked.pid AS blocked_pid, blocked.query AS blocked_query, blocking.pid AS blocking_pid, blocking.query AS blocking_query FROM pg_locks blocked_locks JOIN pg_stat_activity blocked ON blocked.pid = blocked_locks.pid JOIN pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype AND blocking_locks.pid != blocked_locks.pid JOIN pg_stat_activity blocking ON blocking.pid = blocking_locks.pid WHERE NOT blocked_locks.granted;
拿到 blocking_pid 后,立刻终止:
- 温和终止:
SELECT pg_cancel_backend(blocking_pid);(中断当前查询,事务仍存在) - 强制终止:
SELECT pg_terminate_backend(blocking_pid);(杀掉整个连接,事务回滚)
注意:pg_terminate_backend 会丢弃未提交变更,仅用于紧急恢复;日常应靠代码修复,而非依赖 kill。
长事务阻塞最难察觉的点在于它不报错、不占 CPU、日志里也无明显异常——只表现为“接口变慢+连接池满”。排查时务必跳过应用层指标,直奔数据库的 pg_stat_activity,否则永远在修漏水的桶。











