PostgreSQL DELETE 查询挂起(EXPLAIN ANALYZE 也是如此!)

aar*_*rkk 5 postgresql

我正在尝试从匹配查询的表中删除一堆行。我的查询的一般形式是:

DELETE FROM mytable WHERE _id IN (SELECT _id FROM mytable WHERE ...);

_id列是一个SERIAL PRIMARY KEY.

如果我自己运行内部SELECT查询,它会在大约 1 秒内运行并返回大约 100,000 行。但是,当我将DELETE调用添加到它时,它似乎只是坐下来大口大口地走开。大口大口地走开。我让它运行了大约一分钟左右,然后假设没有取得任何进展就取消了它。

我添加EXPLAIN ANALYZE到它的前面,看看我是否可以检查什么花了这么长时间,但那个电话也挂了很长时间。

任何人都可以告诉我可能会发生什么,以便可以非常快速地识别我想要删除的行,但需要不确定的时间来删除它们?

更新:完整查询如下。

DELETE FROM cards 
WHERE _id IN (
    SELECT _id
    FROM cards
    LEFT JOIN game_results ON game_results.card_id = cards._id 
    WHERE NOT available
    AND game_id IS NULL
)
Run Code Online (Sandbox Code Playgroud)

表格的简化版本是:

CREATE TABLE cards (_id INTEGER PRIMARY KEY, available BOOLEAN);
CREATE TABLE games (_id INTEGER PRIMARY KEY);
CREATE TABLE game_results (game_id INTEGER REFERENCES games, card_id INTEGER REFERENCES cards);
Run Code Online (Sandbox Code Playgroud)

我确信这个查询最终会完成运行,我只是惊讶于检索所有要删除的 ID 需要多长时间。

解决方案:

问题是三重的!

  1. 我运行了一个由中止脚本触发的较早(非常慢!)查询。即使脚本已中止,查询仍在运行。因此,尝试删除索引之类的操作被锁定,等待查询完成。使用手动取消该查询pg_cancel_backend允许我尝试删除索引和外键约束。

  2. 外键约束似乎是减缓一切的问题。game_results引用的表cards使删除永远持续的事实。有趣的是,我只是删除了在 game_results 中明确未使用的卡片,但当然这并没有阻止外键检查在删除时发生!这让事情变得非常缓慢。我在运行删除之前删除了外键约束,这将事情加速到我认为合适的水平。

  3. 无论出于何种原因,已接受答案中的第二个删除查询比第一个运行得快得多。

通过结合这三件事,我能够在几秒钟内完成整个删除,而在我将这三个因素结合在一起之前,我的查询运行到 5-10 分钟(在我取消它们之前!)。

感谢大家的帮助!

ype*_*eᵀᴹ 8

假设列不可为空,查询可以简化(没有自连接)使用NOT INNOT EXISTS

DELETE FROM cards 
WHERE available = FALSE 
  AND _id NOT IN 
      ( SELECT card_id
        FROM game_results 
      ) ;

DELETE FROM cards AS c 
WHERE c.available = FALSE
  AND NOT EXISTS 
      ( SELECT * 
        FROM game_results AS gr
        WHERE gr.card_id = c._id
      ) ;
Run Code Online (Sandbox Code Playgroud)

尽管如此,删除 100K 行不能瞬间完成。性能可能会受到缺少(或过多)索引、触发器、级联删除等的影响。

您可以使用EXPLAIN(only, without ANALYZE) 来获取DELETE. 查看EXPLAIN详情,特别是如果你想尝试ANALYZEEXPLAIN 或其他选项:

重要提示:请记住,ANALYZE使用选项时实际上会执行该语句。尽管EXPLAIN将丢弃 SELECT 将返回的任何输出,但该语句的其他副作用将照常发生。如果您希望EXPLAIN ANALYZEINSERT, UPDATE, DELETE, CREATE TABLE AS, 或EXECUTE语句上使用而不让命令影响您的数据,请使用以下方法:

BEGIN;
EXPLAIN ANALYZE ...;
ROLLBACK;
Run Code Online (Sandbox Code Playgroud)

  • @aardvarkk 很有可能是当时阻止删除的原因 (2认同)

Jac*_*las 5

检查没有什么阻止DELETE

查看 pg_locks 向您显示授予了哪些锁以及哪些进程正在等待获取锁。开始寻找锁定问题的一个很好的查询:

SELECT relation::regclass, * FROM pg_locks WHERE NOT GRANTED;
Run Code Online (Sandbox Code Playgroud)