Postgres中使用游标批量删除

she*_*Roy 4 postgresql cursor sql-delete

我是 Postgres 新手,有一项任务是从未分区创建的活动表中删除记录。现在我的想法是创建一个游标来以受控的方式删除记录。

我脑子里的步骤:

  • 声明一个游标,打开它
  • 将记录提取到游标中,其中将有一个日期过滤器
  • 通过受控记录例如 5000/事务来删除语句
  • 循环直到初始获取完成
  • 退出循环并关闭光标

这有意义吗?

Erw*_*ter 11

只是DELETE。忘记光标。

\n

除非您的表很大并且要删除很大比例的行,否则批量执行此操作是没有意义的。即便如此,也几乎没有任何意义。我能想到的唯一原因是允许VACUUM提前删除死元组,这在特​​殊情况下可能会有所帮助。或者避免锁定争用(可能的死锁?),但这引出了一个问题:为什么要删除的行首先应该由并发事务锁定。

\n

为此,每行都有锁。并发INSERTUPDATE不同不存在冲突。(如果您有针对某些相同行的更新,则会遇到更大的问题。)并且编写器不会阻止读取器在 Postgres 上阻止读者。

\n

可以使用该选项创建SQL 游标WITH HOLD,然后在单独的事务中使用它DELETE ... WHERE CURRENT OF。但无论如何,您都必须投入FOR UPDATE,锁定所有受影响的行。很少有意义,除非您想快速锁定所有受影响的行,但在删除之前仍对它们执行某些操作,并且可能有更聪明的方法......

\n

在单独的事务中对大数据进行分区是有意义的UPDATE,以便可以在热更新中重用死元组(在手动更新之后VACUUMautovacuum已启动)。但这几乎不适用于DELETE不重复使用空间的操作。而且,DELETE速度比 快很多UPDATE

\n

万一您仍然需要批量执行此操作,仍然不要使用游标。使用类似的东西:

\n
WITH cte AS (\n   SELECT id                 -- your PK\n   FROM   tbl\n   WHERE  date < $something  -- your condition\n   -- ORDER BY ???           -- optional, see below\n   LIMIT  50000\n   FOR    UPDATE             -- SKIP LOCKED ?\n   )\nDELETE FROM tbl\nUSING  cte\nWHERE  tbl.id = cte.id;\n
Run Code Online (Sandbox Code Playgroud)\n

重复直到找不到行。

\n

如果您的数据(大部分)以某种方式进行物理排序,则可以相应地对行进行排序(引用的ORDER BY)。这ORDER BY会带来自己的成本,但每个都DELETE可能能够访问更少的具有聚集行的数据页,并且这样速度更快。取决于用例;如果ORDER BY能用指数的话,前景会更好。

\n

看:

\n\n