【问题标题】:write to a postgresql database table or csv file? Which is faster?写入 postgresql 数据库表或 csv 文件?哪个更快?
【发布时间】:2021-02-04 10:36:48
【问题描述】:

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

【问题讨论】:

  • 简短的回答是“是”。在当前负载运行时在另一个数据库中尝试。
  • 请您给我一个大概的估计时间。粗略估计我的意思是需要几个小时或几天?该脚本在 Pycharm 和一个 venv 中运行,因此运行第二个实例需要创建一个新的 venv、修改代码、安装必要的依赖项、创建数据库和表。
  • 你的 python 脚本从哪里获取数据?您的数据库在哪里(本地/远程)? csv 文件的文件大小是多少?有很多变量可以回答这个问题。
  • 6 天?你要写多少TB?您使用的是单个 INSERT 语句还是 COPY?最后一个是快速点亮
  • 单次插入 = 慢。检查副本以及如何使用它。不确定您使用的是什么适配器,但这个可以正常工作:psycopg.org/docs/usage.html#using-copy-to-and-copy-from

标签: python python-3.x postgresql csv


【解决方案1】:

关于INSERT的速度:

您的客户端程序执行的每个查询都会导致通过网络进行通信,或者在 localhost 上使用 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;

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

CREATE INDEX foo_ip ON foo(ip);

需要额外的 17 秒,

ALTER TABLE foo SET LOGGED;

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

【讨论】:

  • 谢谢大家。我将 1360 万行写入 csv 文件并使用 pgadmin 导入文件。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-14
  • 2023-03-17
  • 1970-01-01
相关资源
最近更新 更多