无法访问 postgresql 表

Zsá*_*ila 2 python database postgresql psycopg2 digital-ocean

简单的查询运行了几个小时,但只针对一张表,这可能是什么原因?

我尝试删除它,显然它也被停滞了,而且我无法对该表执行任何其他基本或复杂的查询。我在 digitalocean,所以我不能只在数据库上重新启动。我使用 python 从 URL 导入了此表,但在运行脚本之前忘记关闭 psycopg2 连接,因此我不得不终止它,也许这可能导致问题。

顺便提一句。这是我的个人数据库,我没有其他管理员可以将我锁定或其他任何东西。我还查看了日志,但没有任何异常活动的迹象。不过,这就是它,原来是德文的,我必须翻译它。

09:18:35.644:PostgreSQL 14.2,由 Visual C++ build 1914 编译,64 位启动

09:18:35.645:IPv6 地址“::”、端口 25060 上的预期连接

09:18:35.645:期待 IPv4 地址“0.0.0.0”、端口 25060 上的连接

09:18:35.691:数据库系统中断;最后已知操作时间为 2022-06-16 23:54:18 CEST

09:18:36.137: 数据库系统未正常关闭;正在进行自动恢复

09:18:36.140:重做从 0/172B560 开始

09:18:36.141:0/172B598 处的记录长度无效:预期 24,收到 0

09:18:36.141:重做在 0/172B560 完成 系统使用情况:CPU:用户:0.00 秒,系统:0.00 秒,已用时间:0.00 秒

09:18:36.164:数据库系统已准备好接受连接

20:34:07.203:请求快速关闭

20:34:07.208:任何活动事务都被中止

20:34:07.232:后台工作程序“逻辑复制启动器”(PID 15088)终止,状态为 1

20:34:07.241:关闭

20:34:07.292:数据库系统关闭

THX*_*138 5

您可能有一个孤立的空闲进程,该进程已获取表上的,但由于未提交的事务而尚未释放该锁,并且永远不会被释放,因为您无法提交它。

如果像这样的简单查询SELECT * FROM table被阻塞,那么您很可能会在您取消的脚本中运行的Access Exclusive Lock诸如ALTER/语句之类的内容中出现一个表。DROP

您可以通过运行以下命令来验证这一点:

SELECT
    psa.pid,
    psa.query,
    psa.state,
    pg_locks.mode lock_mode,
    relation::regclass locked_relation

FROM
  pg_locks
  JOIN pg_stat_activity psa on pg_locks.pid = psa.pid

WHERE
  granted
  and 'my_schema.my_table'::regclass = relation
Run Code Online (Sandbox Code Playgroud)

然后你可以杀死任何在你的表上有锁的进程:

SELECT
    pg_terminate_backend(psa.pid),

FROM
  pg_locks
  JOIN pg_stat_activity psa on pg_locks.pid = psa.pid

WHERE
  granted
  and 'my_schema.my_table'::regclass = relation
Run Code Online (Sandbox Code Playgroud)

如果仍有阻塞的查询,您可以在阻塞的查询运行时打开一个新连接,并执行以下命令:

SELECT
    activity.pid pid,
    activity.state state,
    activity.query blocked_query,
    blocking_pid,
    blocking_activity.state blocking_state,
    blocking_activity.query blocking_query,
    locks.locked_relations blocking_relation_locks
FROM
 pg_stat_activity activity
 JOIN LATERAL unnest(pg_blocking_pids(activity.pid)) blocking_pids(blocking_pid) ON TRUE
 JOIN pg_stat_activity blocking_activity ON (blocking_pids.blocking_pid = blocking_activity.pid)
 JOIN LATERAL (
     SELECT
        string_agg(locks.relation::regclass::text,',') locked_relations
     FROM

       pg_locks locks

     WHERE
         locks.pid = blocking_pid

     GROUP BY
       blocking_pid
) locks ON TRUE;
Run Code Online (Sandbox Code Playgroud)

这将向您显示阻塞另一个进程的每个进程。一旦找到有问题的进程,您可以使用以下方法消灭它们pg_terminate_backend(pid)

如果你想杀死阻塞另一个 pid 的每个 pid,你可以运行:

SELECT 
  pg_terminate_backend(unnest(pg_blocking_pids(pid)))
FROM
  pg_stat_activity 
Run Code Online (Sandbox Code Playgroud)

如果没有其他关键进程正在运行,并且为了简单起见想要消除每个数据库连接,请执行以下操作:

SELECT
  pg_terminate_backend(pg_stat_activity.pid)
FROM
  pg_stat_activity
WHERE
  --delete this line if you want to kill all connections on the whole cluster
  pg_stat_activity.datname = '<your database name>'
  AND pid <> pg_backend_pid();
Run Code Online (Sandbox Code Playgroud)

完成后,您将仅拥有执行此查询的连接。

如果上述步骤都不起作用,则可能 - 正如 Laurenz Albe 在评论中指出的那样 - 您可能有一个准备好的事务正在锁定您的表。准备好的事务创建的锁可以在后端终止时继续存在,甚至服务器重新启动。它们必须显式回滚或提交。

您可以使用以下查询检查准备好的事务是否锁定任何表

SELECT
  ptx.transaction prep_tx_id,
  ptx.gid prep_tx_name,
  locks.mode lock_mode,
  locks.relation::regclass locked_relation
FROM
  pg_prepared_xacts ptx
  JOIN pg_locks tx ON ptx.transaction = tx.transactionid and tx.locktype = 'transactionid'
  JOIN pg_locks locks ON tx.virtualtransaction = locks.virtualtransaction

WHERE
  locks.granted
  and locks.relation = 'my_schema.my_table'::regclass
Run Code Online (Sandbox Code Playgroud)

然后您可以回滚准备好的事务,例如:

ROLLBACK PREPARED '<TRANSACTION_NAME>' 
Run Code Online (Sandbox Code Playgroud)