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

Ric*_*rdo 31 sql postgresql sql-update

我正在UPDATE table SET column1 = 0Postegres 8.4上尝试一个简单的约300万行的桌子,但它需要永远完成.它已运行超过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)
);
Run Code Online (Sandbox Code Playgroud)

我想跑 UPDATE myTable SET generalFreq = 0;

Le *_*oid 41

我必须为每行更新具有不同值的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.
    以下是操作顺序:

CREATE TEMP TABLE tempTable (id BIGINT NOT NULL, field(s) to be updated,
CONSTRAINT tempTable_pkey PRIMARY KEY (id));
Run Code Online (Sandbox Code Playgroud)

根据可用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;
Run Code Online (Sandbox Code Playgroud)

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

  • 我今天遇到了类似的问题,通过执行您描述的更改(累积临时表中的更改,然后从中更新),我们看到性能提高了100倍.2.5小时到2分钟左右. (5认同)
  • 抱歉 - 我不太了解缓冲区部分。您能否添加有关如何“*在缓冲区中累积一堆更新*”的更多信息? (4认同)
  • @n1000 缓冲区可以是 CSV 文件或 Python 中的 [StringIO 对象](https://docs.python.org/3.5/library/io.html#io.StringIO)。缓冲区的每一行将包含与“tempTable”相同的数据结构,以使 Postgresql [COPY 命令](http://www.postgresql.org/docs/current/static/sql-copy.html) 工作。 (4认同)

Fra*_*ens 16

看看这个答案:PostgreSQL在一个包含数组和大量更新的大型表上变慢

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

SELECT n_tup_hot_upd, * FROM pg_stat_user_tables WHERE relname = 'myTable';
Run Code Online (Sandbox Code Playgroud)

当您有大量要更新的记录时,HOT更新会快得多.有关HOT的更多信息,请参阅本文.

PS.您需要8.3或更高版本.


Ric*_*rdo 9

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

CREATE TABLE table2 AS 
SELECT 
  all the fields of table1 except the one I wanted to update, 0 as theFieldToUpdate
from myTable
Run Code Online (Sandbox Code Playgroud)

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

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

  • Postgresql的MVCC实现使更新变得昂贵.如果要更新表中的每一行,则需要将每行复制为新版本,并将旧版本标记为已删除.因此,重写表格更快(例如,更改列的类型会自动更改)并不奇怪.你可以做的不多,只是一个要注意的性能特征. (6认同)

Tre*_*reg 7

今天我花了很多时间来解决类似的问题。我找到了一个解决方案在更新之前删除所有约束/索引。无论正在更新的列是否已建立索引,psql 似乎都会更新所有已更新行的所有索引。更新完成后,添加回约束/索引。