在大桌子上非常慢的Postgres UPDATE

时间:2012-03-10 21:23:35

标签: postgresql sql-update

我有一个Postgres 9.1.3表,在 WHERE Y = 1 之后有206万行,如下所示(它总共只有几万行没有任何 WHERE )。我正在尝试使用如下查询将数据添加到空字段:

WITH B AS (
    SELECT Z,
           rank() OVER (ORDER BY L, N, M, P) AS X
    FROM   A
    WHERE  Y=1
)

UPDATE A
SET A.X = B.X
FROM B
WHERE A.Y=1
  AND B.Z = A.Z;

此查询运行数小时,似乎进展非常缓慢。事实上,我第二次尝试这个,在查询运行了大约3个小时之后我停电了。恢复供电后,我分析了桌子并得到了这个:

INFO:  analyzing "consistent.master"
INFO:  "master": scanned 30000 of 69354 pages, containing 903542 live rows and 153552 dead rows; 30000 rows in sample, 2294502 estimated total rows
Total query runtime: 60089 ms.

判断查询在那几个小时内几乎没有进展是否正确?

在运行长查询之前,我已经完成了 VACUUM FULL ANALYZE

WITH 中的查询只需要40秒。

上面引用的所有字段除了A.X和扩展名B.X之外都被编入索引:L,M,N,P,Y,Z。

这是在具有8 GB RAM,Core i7 Q720 1.6 GHz四核处理器和Windows 7 x64的笔记本电脑上运行。我正在运行Postgres 32位以与PostGIS 1.5.3兼容。 64位PostGIS for Windows尚不可用。 (32位Postgres意味着它在Windows中不能使用超过2 GB的RAM,但我怀疑这是一个问题。)

以下是EXPLAIN的结果:

Update on A  (cost=727684.76..945437.01 rows=2032987 width=330)
  CTE B
    ->  WindowAgg  (cost=491007.50..542482.47 rows=2058999 width=43)
          ->  Sort  (cost=491007.50..496155.00 rows=2058999 width=43)
                Sort Key: A.L, A.N, A.M, A.P
                ->  Seq Scan on A  (cost=0.00..85066.80 rows=2058999 width=43)
                      Filter: (Y = 1)
  ->  Hash Join  (cost=185202.29..402954.54 rows=2032987 width=330)
        Hash Cond: ((B.Z)::text = (A.Z)::text)
        ->  CTE Scan on B  (cost=0.00..41179.98 rows=2058999 width=88)
        ->  Hash  (cost=85066.80..85066.80 rows=2058999 width=266)
              ->  Seq Scan on A  (cost=0.00..85066.80 rows=2058999 width=266)
                    Filter: (Y = 1)

3 个答案:

答案 0 :(得分:4)

可能有多种解决方案。

  • 锁定时可能会阻止更新。请参阅pg_locks视图。
  • 也许A上有触发器?他们可能是放缓的原因。
  • 尝试“解释更新......” - 该计划是否明显不同于普通选择计划?也许你可以分2步完成 - 将“B”导出到表中,然后从该表中更新。
  • 尝试在更新前删除索引。
  • 创建一个新表,删除旧表,将新表重命名为旧表的名称。

答案 1 :(得分:0)

尝试重写这样的查询:

UPDATE A
SET A.X = B.X
FROM B
WHERE A.Y=1
      AND B.Z = A.Z
      AND A.X IS DISTINCT FROM B.X;

答案 2 :(得分:0)

WITH B AS (
  
  -- list all the columns in your table in order including the one to be "updated"
  SELECT L, N, M, P,
         rank() OVER (ORDER BY L, N, M, P) AS X,
         Y, Z
  FROM   A
  WHERE  Y=1

), D AS (

   DELETE FROM A WHERE Y=1

)
INSERT INTO A
SELECT * FROM B

我现在已经使用了几次来修复一个永无止境的更新。上述模式在几分钟内完成了数百万行。

其中一次我遇到了主键冲突。通过在运行上述语句之前DROP处理主键并在之后重新CREATE处理它来解决这个问题。我很惊讶这是必要的,因为在我的 CTE 中,冲突的值在插入发生之前被删除,但哦,好吧。