在PostgreSQL数据库上缓慢简单的更新查询,有300万行

时间:2010-07-29 10:01:43

标签: sql postgresql sql-update

我正在Postegres 8.4上的一个约300万行的桌子上尝试一个简单的UPDATE table SET column1 = 0,但它需要永远完成。它已运行超过10分钟。现在是我的最后一次尝试。

之前,我试图在该表上运行VACUUM和ANALYZE命令,我也试图创建一些索引(虽然我怀疑这会在这种情况下有所不同)但似乎没有任何帮助。

还有其他想法吗?

谢谢, 里卡多

更新

这是表结构:

CREATE TABLE myTable
(
  id bigserial NOT NULL,
  title text,
  description text,
  link text,
  "type" character varying(255),
  generalFreq real,
  generalWeight real,
  author_id bigint,
  status_id bigint,
  CONSTRAINT resources_pkey PRIMARY KEY (id),
  CONSTRAINT author_pkey FOREIGN KEY (author_id)
      REFERENCES users (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT c_unique_status_id UNIQUE (status_id)
);

我正在尝试运行UPDATE myTable SET generalFreq = 0;

9 个答案:

答案 0 :(得分:31)

我必须使用每行的不同值更新1或20亿行的表。每次运行都会产生约1亿次变化(10%)。 我的第一次尝试是将它们直接分配到特定分区上的300K更新事务中,因为如果使用分区,Postgresql并不总是优化准备好的查询。

  1. 一堆“UPDATE myTable SET myField = value WHERE”的事务 myId = id“
    给予 1,500 更新/秒。这意味着每次运行都会 至少需要18个小时。
  2. HOT更新解决方案,如此处所述,FILLFACTOR = 50。给 1,600次更新/秒。我使用SSD,因此它的成本很高 存储空间增加一倍。
  3. 插入更新值的临时表并在之后合并 使用UPDATE ... FROM提供 18,000 更新/秒。如果我做VACUUM 对于每个分区;否则为100,000 up / s。 Cooool。这是 操作顺序:

  4. CREATE TEMP TABLE tempTable (id BIGINT NOT NULL, field(s) to be updated,
    CONSTRAINT tempTable_pkey PRIMARY KEY (id));
    

    根据可用的RAM累积缓冲区中的一堆更新 当它填满,或需要更改表/分区,或完成时:

    COPY tempTable FROM buffer;
    UPDATE myTable a SET field(s)=value(s) FROM tempTable b WHERE a.id=b.id;
    COMMIT;
    TRUNCATE TABLE tempTable;
    VACUUM FULL ANALYZE myTable;
    

    这意味着现在运行需要1.5小时而不是18小时才能进行1亿次更新,包括真空。

答案 1 :(得分:13)

看一下这个答案:PostgreSQL slow on a large table with arrays and lots of updates

首先从更好的FILLFACTOR开始,执行VACUUM FULL强制表重写并在UPDATE查询后检查HOT更新:

SELECT n_tup_hot_upd, * FROM pg_stat_user_tables WHERE relname = 'myTable';

当您要更新大量记录时,HOT更新速度会快得多。有关HOT的更多信息,请参阅此article

聚苯乙烯。您需要8.3或更高版本。

答案 2 :(得分:7)

等待35分钟后。为了我的UPDATE查询完成(但仍然没有)我决定尝试不同的东西。所以我所做的是一个命令:

CREATE TABLE table2 AS 
SELECT 
  all the fields of table1 except the one I wanted to update, 0 as theFieldToUpdate
from myTable

然后添加索引,然后删除旧表并重命名新表以取代它。那只花了1.7分钟。处理加上一些额外的时间来重新创建索引和约束。但它确实有帮助! :)

当然,这只是因为没有其他人在使用数据库。如果这是在生产环境中,我需要先锁定表。

答案 3 :(得分:2)

今天我花了很多时间处理类似问题。我发现solution删除了更新前的所有约束/索引。无论要更新的列是否已编制索引,似乎psql都会更新所有更新行的所有索引。更新完成后,再添加约束/索引。

答案 4 :(得分:2)

试试这个(注意generalFreq以REAL类型开头,并保持不变):

ALTER TABLE myTable ALTER COLUMN generalFreq TYPE REAL USING 0;

这将重写表,类似于DROP + CREATE,并重建所有索引。但一切都在一个命令中。更快(大约2倍)并且您不必处理依赖关系并重新创建索引和其他东西,尽管它确实锁定了表(访问独占 - 即完全锁定)持续时间。或者也许这就是你想要的东西,如果你想让其他一切排在后面。如果您没有更新“太多”行,则这种方式比仅更新更慢。

答案 5 :(得分:1)

我建议的第一件事(来自https://dba.stackexchange.com/questions/118178/does-updating-a-row-with-the-same-value-actually-update-the-row)只是更新"需要"它,例如:

 UPDATE myTable SET generalFreq = 0 where generalFreq != 0;

(可能还需要generalFreq上的索引)。然后,您将更新更少的行。虽然不是因为这些值都已经非零,但是更新的行数减少了#34;可以帮助"因为否则它会更新它们和所有索引,无论值是否发生变化。

另一种选择:如果星星在默认值和非空约束方面对齐,您可以删除旧列并仅通过调整元数据即时创建another

答案 6 :(得分:0)

你是如何运作的?如果您循环每一行并执行更新语句,那么您可能会运行数百万个单独的更新,这就是为什么它的执行速度会非常慢。

如果您在一个语句中为所有记录运行单个更新语句,它将运行得更快,如果此过程很慢,那么它可能比您的硬件更重要。 300万是很多记录。

答案 7 :(得分:0)

在测试中,我注意到即使有一个临时表,超过20万行的大更新比10万行的2个更新要慢。

我的解决方案是循环,在每个循环中创建一个200,000行的临时表,在该表中我计算我的值,然后使用新值aso更新我的主表...

每2000000行,我都手动进行“ VACUUM ANALYZE mytable”,我注意到自动吸尘器并不能完成此类更新。

答案 8 :(得分:-3)

尝试

UPDATE myTable SET generalFreq = 0.0;

也许这是一个演员问题