sql - 如何加快 PostgreSQL 中的插入性能

标签 sql postgresql bulkinsert sql-insert

我正在测试 Postgres 插入性能。我有一个表,其中一列以数字作为其数据类型。上面也有一个索引。我使用这个查询填满了数据库:

insert into aNumber (id) values (564),(43536),(34560) ...

我用上面的查询非常快地一次插入了 400 万行 10,000。数据库达到 600 万行后,性能急剧下降到每 15 分钟 100 万行。有什么技巧可以提高插入性能吗?我需要这个项目的最佳插入性能。

在具有 5 GB RAM 的机器上使用 Windows 7 Pro。

最佳答案

populate a database在 PostgreSQL 手册中,depesz's excellent-as-usual article关于这个话题,和 this SO question .
(请注意,此答案是关于将数据批量加载到现有数据库中或创建一个新数据库。如果您对 pg_restorepsql 执行 pg_dump 输出的数据库还原性能感兴趣,那么大部分内容都没有' t 适用,因为 pg_dumppg_restore 在完成模式+数据恢复后已经做了诸如创建触发器和索引之类的事情)。
有很多事情要做。理想的解决方案是导入 UNLOGGED没有索引的表,然后将其更改为已记录并添加索引。不幸的是,在 PostgreSQL 9.4 中不支持从 UNLOGGED 更改表。登录。 9.5 添加 ALTER TABLE ... SET LOGGED允许你这样做。
如果您可以将数据库脱机进行批量导入,请使用 pg_bulkload .
否则:

  • 禁用表上的任何触发器
  • 在开始导入之前删除索引,然后重新创建它们。 (与逐步向其中添加相同数据相比,一次构建索引所需的时间要少得多,并且生成的索引要紧凑得多)。
  • 如果在单个事务中执行导入,则在提交之前删除外键约束、执行导入并重新创建约束是安全的。如果导入被拆分为多个事务,则不要这样做,因为您可能会引入无效数据。
  • 如果可能,请使用 COPY而不是 INSERT
  • 如果您不能使用 COPY考虑使用多值 INSERT如果实用。你似乎已经在这样做了。不要试图在单个 VALUES 中列出太多值尽管;这些值必须多次放入内存中,因此每个语句保持几百个。
  • 将您的插入分批处理到显式事务中,每个事务执行数十万或数百万次插入。 AFAIK 没有实际限制,但批处理可以让您通过在输入数据中标记每个批次的开始来从错误中恢复。同样,您似乎已经在这样做了。
  • 使用 synchronous_commit=off和一个巨大的 commit_delay减少 fsync() 成本。但是,如果您将工作分批处理为大事务,这将无济于事。
  • INSERTCOPY从几个连接并行。多少取决于您的硬件的磁盘子系统;根据经验,如果使用直接附加存储,您需要每个物理硬盘驱动器一个连接。
  • 设置高max_wal_size值(旧版本中为 checkpoint_segments)并启用 log_checkpoints .查看 PostgreSQL 日志并确保它没有提示检查点发生得太频繁。
  • 如果且仅当您不介意在导入过程中系统崩溃时丢失整个 PostgreSQL 集群(您的数据库和同一集群上的任何其他集群)到灾难性损坏,您可以停止 Pg,设置 fsync=off ,启动 Pg,进行导入,然后(重要地)停止 Pg 并设置 fsync=on再次。见 WAL configuration . 如果 PostgreSQL 安装上的任何数据库中已经有任何您关心的数据,请不要这样做。 如果您设置 fsync=off您也可以设置 full_page_writes=off ;同样,请记住在导入后重新打开它以防止数据库损坏和数据丢失。见 non-durable settings在 Pg 手册中。

  • 您还应该考虑调整系统:
  • 尽可能使用质量好的 SSD 进行存储。具有可靠、受电源保护的回写缓存的优质 SSD 使提交率非常快。当您遵循上述建议时,它们的好处会减少 - 这会减少磁盘刷新/数量 fsync() s - 但仍然可以提供很大帮助。不要使用没有适当断电保护的廉价 SSD,除非您不关心保存数据。
  • 如果您将 RAID 5 或 RAID 6 用于直接连接存储,请立即停止。备份您的数据,将您的 RAID 阵列重组为 RAID 10,然后重试。 RAID 5/6 对批量写入性能毫无希望——尽管具有大缓存的良好 RAID Controller 会有所帮助。
  • 如果您可以选择使用带有大容量电池支持的回写缓存的硬件 RAID Controller ,这可以真正提高具有大量提交的工作负载的写入性能。如果您使用带有 commit_delay 的异步提交,或者在批量加载期间执行较少的大事务,则没有多大帮助。
  • 如果可能,将 WAL( pg_wal 或旧版本中的 pg_xlog)存储在单独的磁盘/磁盘阵列上。在同一磁盘上使用单独的文件系统没什么意义。人们通常选择将 RAID1 对用于 WAL。同样,这对具有高提交率的系统影响更大,如果您使用未记录的表作为数据加载目标,则影响很小。

  • 您可能还对 Optimise PostgreSQL for fast testing 感兴趣.

    关于sql - 如何加快 PostgreSQL 中的插入性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12206600/

    相关文章:

    postgresql - 为什么这个使用继承的外键不起作用?

    带有 Postgresql 的 django UUIDField

    java - 将日期插入 Access DB 时出现问题 - 由于时间变化,小时不存在

    python - Elasticsearch批量插入失败时回滚

    mysql - 如何用MySQL中以前的非零值替换零值?

    mysql - 在不同的行上选择满足不同条件的值?

    Java网络编程

    java - 从 SQL 查询方法返回泛型类型

    postgresql - 如何显示连接到 PostgreSQL 服务器的用户列表

    sql - 使用批量插入在 oracle 中的表之间移动大数据