【问题标题】:Very slow Postgres UPDATE on large table大表上的 Postgres UPDATE 非常慢
【发布时间】:2012-03-10 21:23:34
【问题描述】:

我有一个 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 FULLANALYZE

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 兼容。 Windows 的 64 位 PostGIS 尚不可用。 (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)

【问题讨论】:

  • 我看不懂这些数字。该表包含 200 万行。条件WHERE Y=1从中选择了多少行?
  • 请发布EXPLAIN 或(如果完成)EXPLAIN ANALYZE 输出。
  • 如果Y=1 部分非常有选择性(就像在我随机生成的数据中一样),那么更新会在几毫秒内完成。因此,请提供sscce.org 数据示例。
  • 刚刚做了第三次尝试,让它运行了一整夜。到目前为止,它已经运行了 17.6 小时,但尚未完成。你们都有很好的问题。我必须在今天晚些时候提供答案。
  • 刚刚添加了 EXPLAIN 结果。今晚可能会在 7 个多小时内尝试回到这里,以获取其余的请求信息。

标签: postgresql sql-update


【解决方案1】:

可能有多种解决方案。

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

【讨论】:

  • 我赌的是(b)锁定问题。我在我相当过时的桌面上尝试了更新,用原始语句更新 200 万行花了大约 4 分钟。
  • 我是这个数据库的唯一用户。这不会排除(b)锁定吗?没有触发器。解释结果在我编辑的帖子中。我认为它将UDPATE和SELECT分开?关于第二张桌子的好主意;我看看今天晚些时候能不能试试。
  • 只是为了确保,您写了“在更新前删除索引”。这不会让事情变得更糟吗?更新后的字段 (A.X) 未编入索引,并且我有涉及 A.YA.ZWHERE 子句。跨度>
  • @ArenCambre:如您所见,我使用了您的查询计划 no 索引。之所以如此,是因为 A.Y 非常不具选择性,甚至 A.Z(这似乎是一些唯一的 id)也无济于事,因为无论如何您几乎都更新了完整的表。但我也怀疑,删除索引是否会对您有很大帮助。
  • 哦,哇,我明白你的意思了。我刚刚对另一个带有索引的表的简单查询进行了 EXPLAIN,然后我看到了结果中明确提到了索引的位置。我不清楚为什么索引不起作用。无论如何,我已将@maniek 的答案标记为正确,因为他的最后一个子弹似乎是最佳答案。
【解决方案2】:

尝试像这样重写查询:

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;

【讨论】:

    【解决方案3】:
    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
    

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-17
      • 1970-01-01
      • 2015-12-14
      • 1970-01-01
      • 1970-01-01
      • 2021-12-18
      相关资源
      最近更新 更多