写入 postgresql 数据库表或 csv 文件?哪个更快?

时间:2021-02-04 10:36:48

标签: python python-3.x postgresql csv

我创建了一个 python 脚本,目前正在将 15,000,000 行插入到数据库表中。我计算过这将需要 6 天。它目前有 2,000,000 行,有 3 列类型为 VARCHAR、BOOLEAN、SMALLINT。我想知道,将 1500 万行写入 csv 文件然后导入 csv 文件以创建数据库表是否会更快。我不想取消当前流程,以防万一。我现在 16 小时了。

1 个答案:

答案 0 :(得分:0)

关于INSERT的速度:

您的客户端程序执行的每个查询都会导致通过网络或本地主机上的 unix 套接字进行通信,这会产生一些开销。

此外,这很重要,如果您使用自动提交并且不在事务中包装大量 INSERT,那么每个 INSERT 将在其自己的事务中并且 postgres 不会向客户端确认 INSERT 已完成直到数据以一种保证在服务器崩溃时它仍然存在的方式写入磁盘。这种刷新到磁盘显然需要一段时间,在旋转磁盘上预计至少需要 10 毫秒,SSD 更少,但仍然不是零。如果你做了很多,你真的不想为每个 INSERT 都做,所以首先要做的是将整个事情包装在 BEGIN/COMMIT 中,以便它在单个事务中运行。

接下来要做的是 INSERT INTO ... VALUES () 并将大量值放入其中,而不仅仅是一行,因此查询开销会分摊到多行中。

<块引用>

对于剩余的范围,我生成了每个网络号,因此生成三个八位字节或 24 位网络号并将它们写入数据库。

所以明显的下一个优化是在服务器上生成范围。例如

INSERT INTO table VALUES (1)
...lots of INSERTS...
INSERT INTO table (column) VALUES (100)

这样会更快:

INSERT INTO table (column) VALUES (1),(2),...lots of values...,(100)

这样会更快,但 COPY 仍然需要解析所有数据:

\copy table (column) FROM stdin
1
2
...etc

还有这个:

INSERT INTO table (column) SELECT n FROM generate_series(1,100) n;

会更快,因为它不必将所有文本解析为值,它只会生成它们。如果您想插入范围,并且在客户端上使用循环来执行此操作,请将循环替换为服务器上的 generate_series。

接下来,如果您不需要实际存储每个 ip 地址,而只对范围感兴趣,那么为什么不直接存储范围而不是所有 ip 呢? Postgres 具有 RANGE 功能。这也将大大减少表的大小。

接下来您可以使用 UNLOGGED 表来加速您的插入。它不会有崩溃恢复,这意味着 postgres 不会费心将恢复日志数据写入磁盘,这会大大加快 INSERT 和 UPDATE 的速度。生成所有数据后,您始终可以将其切换回 LOGGED,以便在崩溃时幸免于难。

接下来,在插入所有数据后创建索引。从头开始创建索引比插入数据时一点一点地构建索引要快得多。

我刚刚找到了 this。所以,

CREATE OR REPLACE FUNCTION all_ips(cidr)
RETURNS SETOF inet LANGUAGE SQL IMMUTABLE AS
$$
 select $1 + s from generate_series(1,  broadcast($1) - (network($1)) - 1) s;
$$;

BEGIN;
CREATE UNLOGGED TABLE foo (ip INET NOT NULL);
INSERT INTO foo SELECT all_ips('10.0.0.0/8');
COMMIT;

这个 1600 万行的 INSERT 在 2008 年的 Core 2 Quad 上需要 44 秒。它主要使用 CPU,可能在内部 INET/CIDR 函数中,这意味着您可以并行运行其中一个每个 CPU 内核,以提高速度。 .

CREATE INDEX foo_ip ON foo(ip);

需要额外的 17 秒,

ALTER TABLE foo SET LOGGED;

大约需要一分钟,因为它必须向 WAL 写入大约 1 GB 的表和索引。

相关问题