假设条件
由于Q中缺少信息,因此我假设:
- 您的数据来自数据库服务器上的文件。
- 数据的格式与
COPY
输出一样,每行唯一 id
,以匹配目标表。
如果不是,请先对其进行正确格式化,或者使用COPY
选项进行格式化。
- 您正在更新目标表中的每一行或其中的大多数。
- 您有能力删除并重新创建目标表。
这意味着没有并发访问。否则请考虑以下相关答案:
- 除了索引外,根本没有依赖对象。
解
我建议您采用第三个项目符号链接中概述的类似方法。进行重大优化。
要创建临时表,有一种更简单,更快捷的方法:
CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;
数据库内部UPDATE
临时表中的单个大数据块比数据库外部中的单个更新大几个数量级。
在PostgreSQL的MVCC模型中,一种UPDATE
创建新行版本并将旧版本标记为已删除的方法。这INSERT
和和一样贵DELETE
。此外,它还会留下很多死元组。由于您还是要更新整个表,因此创建一个新表并删除旧表会更快。
如果您有足够的可用temp_buffers
RAM,则在执行其他任何操作之前,请将其高(仅用于该会话!)设置为足以将临时表保留在RAM中。
要估算需要多少RAM,请使用一个小样本运行测试并使用db object size函数:
SELECT pg_size_pretty(pg_relation_size('tmp_tbl')); -- complete size of table
SELECT pg_column_size(t) FROM tmp_tbl t LIMIT 10; -- size of sample rows
完整的脚本
SET temp_buffers = '1GB'; -- example value
CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;
COPY tmp_tbl FROM '/absolute/path/to/file';
CREATE TABLE tbl_new AS
SELECT t.col1, t.col2, u.field1, u.field2
FROM tbl t
JOIN tmp_tbl u USING (id);
-- Create indexes like in original table
ALTER TABLE tbl_new ADD PRIMARY KEY ...;
CREATE INDEX ... ON tbl_new (...);
CREATE INDEX ... ON tbl_new (...);
-- exclusive lock on tbl for a very brief time window!
DROP TABLE tbl;
ALTER TABLE tbl_new RENAME TO tbl;
DROP TABLE tmp_tbl; -- will also be dropped at end of session automatically
并发负载
一旦将表锁定在末尾附近,对表的并发操作(我在开始时的假设中已排除)将等待,并在提交事务后立即失败,因为表名会立即解析为其OID,但是新表具有不同的OID。该表保持一致,但是并发操作可能会出现异常,因此必须重复执行。相关答案的详细信息:
更新路线
如果(必须)走这UPDATE
条路线,请删除更新期间不需要的所有索引,然后再重新创建。在一个索引中创建索引要比为每一行更新索引便宜得多。这也可能允许HOT更新。
我使用提出了一个类似的过程UPDATE
中对SO此密切相关的答案。