PostgreSQL 判断大导入还在跑吗?pg_stat_activity 完整笔记(ERP Staging 场景)
摘要
大批量 ERP 资源导入(Excel → erp_resource_import_staging → 合并正式表)时,Java 应用常因单条大 SQL 长时间无日志,看似"卡死"。
本文基于 pg_stat_activity 提供可直接复制的 SQL,快速判断:导入是否在执行、是否被锁阻塞、是否有空闲事务占锁,并给出安全终止会话方案,覆盖生产全排查场景。
标签:PostgreSQL、慢SQL、数据库锁、ERP导入、运维巡检、pg_stat_activity
一、核心:判断导入是否正在运行
1. 查当前活跃 SQL(首选)
sql
-- 查看正在执行的语句,判断大导入是否在跑
SELECT pid,
usename,
application_name,
state,
wait_event_type,
wait_event,
now() - query_start AS running_for,
left(query, 400) AS query_preview
FROM pg_stat_activity
WHERE datname = current_database()
AND state = 'active'
AND pid <> pg_backend_pid()
ORDER BY query_start;
2. 巡检:只看运行超30秒的大导入
sql
-- 筛选长时间运行的慢导入SQL
SELECT pid,
now() - query_start AS running_for,
query
FROM pg_stat_activity
WHERE datname = current_database()
AND state = 'active'
AND now() - query_start > interval '30 seconds'
AND pid <> pg_backend_pid()
ORDER BY query_start;
快速判断
state='active' + 包含 erp_resource/staging:导入正在执行running_for持续增长:SQL正常运行,未卡死- 无结果:导入已结束/连接断开,应用无日志属正常
二、隐藏坑点:空闲事务占锁(必查)
不是活跃状态,也会卡死导入!
idle in transaction:事务已开启,但未提交/未回滚,长期持有锁不释放。
3. 查空闲未提交事务
sql
-- 排查:空闲但占着锁的事务
SELECT pid,
usename,
state,
now() - xact_start AS xact_age,
now() - state_change AS state_age,
left(query, 200) AS last_query
FROM pg_stat_activity
WHERE datname = current_database()
AND pid <> pg_backend_pid()
AND state = 'idle in transaction'
ORDER BY xact_start;
三、导入卡住?查锁与阻塞链
4. 粗查:是否在等锁
sql
-- 有结果 = 存在会话等待锁
SELECT * FROM pg_locks WHERE NOT granted;
5. 精查:谁阻塞谁(生产最常用)
sql
-- 查看完整阻塞链:被阻塞会话 + 阻塞源会话
SELECT blocked_locks.pid AS blocked_pid,
blocked_activity.usename AS blocked_user,
blocking_locks.pid AS blocking_pid,
blocking_activity.usename AS blocking_user,
blocked_activity.query AS blocked_statement,
blocking_activity.query AS blocking_statement
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks
ON blocking_locks.locktype = blocked_locks.locktype
AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database
AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;
关键:优先处理
blocking_pid(阻塞源头),导入才能恢复。
四、安全终止导入(Kill 规范)
6. 取消 vs 终止命令对比
| 命令 | 作用 | 后果 | 优先级 |
|---|---|---|---|
pg_cancel_backend(pid) |
取消当前执行的SQL | 语句中断,事务回滚,连接保留 | 最高 |
pg_terminate_backend(pid) |
强制断开数据库连接 | 连接销毁,全部未提交事务回滚 | 最低(慎用) |
使用示例
sql
-- 温和取消:推荐使用
SELECT pg_cancel_backend(12345);
-- 强制断开:极端场景使用
SELECT pg_terminate_backend(12345);
⚠️ 注意:
- PID 来自
pg_stat_activity.pid,禁止杀自己当前会话 - 大导入在一个事务内,终止会整批回滚
- 优先顺序:业务停任务 → 等待结束 →
cancel→terminate
五、ERP 大批量导入专属备忘(重点)
- 关键词匹配 :语句包含
erp_resource_import_staging/erp_resource/erp_resource_sku→ 100% 为 Staging 合并导入 - 并行阻塞:多条长时间 INSERT 同时运行 → 大概率重复导入/多实例并行,互锁导致变慢
- 数据一致性 :导入中途
terminate会导致导入记录与库内数据不一致,需重新执行导入
六、排查小结(一分钟速查)
- 看活不活:用第1、2节查活跃SQL
- 看占锁:用第3节查空闲事务
- 看卡住:用第4、5节查锁与阻塞链
- 停导入 :优先
pg_cancel_backend,少用强制终止
极简速查版(收藏备用)
sql
-- 1. 看活跃导入
SELECT pid,state,running_for,left(query,400) FROM pg_stat_activity WHERE state='active' AND datname=current_database();
-- 2. 看空闲占锁事务
SELECT * FROM pg_stat_activity WHERE state='idle in transaction';
-- 3. 看阻塞
SELECT * FROM pg_locks WHERE NOT granted;
-- 4. 安全取消
SELECT pg_cancel_backend(PID);