PostgreSQL单表查询卡顿、无法访问的故障原因咨询
问题根因
故障本质是强制终止导入脚本时,对应数据库连接未正常触发事务回滚和连接退出,残留了持有目标表AccessExclusiveLock的僵尸会话。所有对该表的读写、DDL操作都会排队等待这个排他锁释放,因此会无限期卡住。
日志中记录的WAL无效记录、异常自动恢复是实例此前异常关机的历史日志,对应恢复流程已经执行完成、实例正常对外提供服务,和本次单表访问故障无关联。
排查步骤
- 连接数据库后执行以下SQL,定位持有锁的阻塞会话:
SELECT blocked.pid AS blocked_pid, blocked.query AS blocked_query, blocking.pid AS blocking_pid, blocking.query AS blocking_query, blocking.state AS blocking_state, blocking.wait_event_type AS blocking_wait_type, blocking.wait_event AS blocking_wait_event FROM pg_locks blocked_lock JOIN pg_stat_activity blocked ON blocked.pid = blocked_lock.pid JOIN pg_locks blocking_lock ON blocking_lock.locktype = blocked_lock.locktype AND blocking_lock.database IS NOT DISTINCT FROM blocked_lock.database AND blocking_lock.relation IS NOT DISTINCT FROM blocked_lock.relation AND blocking_lock.pid != blocked_lock.pid JOIN pg_stat_activity blocking ON blocking.pid = blocking_lock.pid WHERE NOT blocked_lock.granted;
- 查询结果中重点筛选状态为
idle in transaction、关联query为你之前执行的导入语句、无有效等待事件的会话,即为残留的锁持有会话。
修复方案
- 拿到阻塞会话的pid后,执行以下SQL终止该僵尸会话,DigitalOcean托管数据库的默认管理员账号有权限执行该操作,无需重启实例:
SELECT pg_terminate_backend(替换为上一步查到的阻塞会话pid);
- 执行完成后再次运行排查步骤的锁查询SQL,确认目标表无残留未释放的锁,此时对该表的查询、删除操作即可正常执行。
规避建议
- 后续中断运行中的数据库操作脚本时,优先通过数据库侧终止对应会话,不要直接强杀本地脚本进程,避免连接未正常退出导致事务和锁残留。
- 使用psycopg2编写导入脚本时,建议配置
statement_timeout参数限制单语句最长执行时间,同时增加异常捕获逻辑,脚本异常退出时主动提交/回滚事务、关闭连接。
内容的提问来源于stack exchange,提问作者Zsámboki Attila
相关产品推荐
相关产品推荐

