用于标识要删除的行的系统列“ctid”是否合法?

rue*_*ehr 8 postgresql performance delete concurrency postgresql-performance

我有一个包含数亿行的表,我需要从中删除数据。

现有的索引是最有效的。

但是,我可以使用现有索引通过使用ctid值查找要删除的行:

DELETE FROM calendar_event WHERE ctid IN
(SELECT ctid FROM calendar_event WHERE user_id = 5 LIMIT 100 FOR UPDATE)
Run Code Online (Sandbox Code Playgroud)

ctid在这种情况下依赖 的风险是什么?我最糟糕的情况是删除错误的行。

Erw*_*ter 9

所采用的ROW SHAREFOR UPDATE可防止并发写访问会更改行的物理位置。手册:

这可以防止它们被其他事务锁定、修改或删除,直到当前事务结束。也就是说,其他尝试UPDATE, DELETE, SELECT FOR UPDATE, SELECT FOR NO KEY UPDATE,SELECT FOR SHARESELECT FOR KEY SHARE这些行的事务将被阻塞,直到当前事务结束;

因此ctid,除非您自己更改同一事务中的行,否则在命令(或事务,甚至)期间应该是稳定的。ctid仍然是内部使用的系统列,项目将不提供任何保证。如果您有任何唯一的(组合)列(包括 PK),请使用它而不是ctid.

但是,我会使用 CTE 来实现选择并避免意外结果。

无需ORDER BY您选择任意行进行删除。您不妨添加SKIP LOCKED以最大程度地减少并发事务的锁争用:

WITH cte AS (
   SELECT ctid
   FROM   calendar_event
   WHERE  user_id = 5
   LIMIT  100
   FOR    UPDATE SKIP LOCKED
   )
DELETE FROM calendar_event WHERE ctid IN (TABLE cte);
Run Code Online (Sandbox Code Playgroud)

相关,并解释了这两种考虑:

关于ctid