我正在测试 Postgres 插入性能。我有一个表,其中有一列以数字作为其数据类型。上面也有索引。我使用此查询填充了数据库:
insert into aNumber (id) values (564),(43536),(34560) ...
我使用上面的查询一次非常快速地插入了 10,000 行 400 万行。在数据库达到 600 万行后,性能急剧下降到每 15 分钟 100 万行。有什么技巧可以提高插入性能吗?我需要这个项目的最佳插入性能。
在具有 5 GB RAM 的计算机上使用 Windows 7 Pro。
请参阅PostgreSQL 手册中的填充数据库、 depesz关于该主题的优秀文章以及这个 SO question。
(请注意,此答案是关于将数据批量加载到现有数据库中或创建新数据库。如果您对使用pg_restore或psql执行pg_dump输出的数据库恢复性能感兴趣,其中大部分内容并不适用,因为pg_dump并且pg_restore已经执行了创建等操作完成模式+数据还原后的触发器和索引)。
pg_restore
psql
pg_dump
有很多事情要做。理想的解决方案是导入一个UNLOGGED没有索引的表,然后将其更改为记录并添加索引。不幸的是,在 PostgreSQL 9.4 中,不支持将表从更改UNLOGGED为记录。9.5 添加ALTER TABLE ... SET LOGGED允许您执行此操作。
UNLOGGED
ALTER TABLE ... SET LOGGED
如果您可以使数据库脱机以进行批量导入,请使用pg_bulkload.
pg_bulkload
除此以外:
禁用表上的任何触发器
在开始导入之前删除索引,然后重新创建它们。(与逐步向其中添加相同数据相比,一次构建索引所需的时间 要 少得多,并且生成的索引要紧凑得多)。
如果在单个事务中执行导入,则删除外键约束、执行导入并在提交之前重新创建约束是安全的。如果导入拆分为多个事务,请不要这样做,因为您可能会引入无效数据。
如果可能,请使用COPY代替INSERTs
COPY
INSERT
如果您不能使用,请COPY考虑在可行的情况下使用多值INSERTs。你似乎已经在这样做了。不要试图在一个单一的列表中列出 太多VALUES的值;这些值必须在内存中适应几次,所以每条语句保持在几百个。
VALUES
将您的插入批处理到显式事务中,每个事务执行数十万或数百万次插入。AFAIK 没有实际限制,但是通过在输入数据中标记每个批次的开始,批处理可以让您从错误中恢复。同样,您似乎已经在这样做了。
使用synchronous_commit=off一个巨大commit_delay的来减少 fsync() 成本。但是,如果您将工作批量处理为大事务,这将无济于事。
synchronous_commit=off
commit_delay
INSERT或COPY从多个连接并行。多少取决于硬件的磁盘子系统;根据经验,如果使用直连存储,您需要每个物理硬盘驱动器一个连接。
设置一个高max_wal_size值(checkpoint_segments在旧版本中)并启用log_checkpoints. 查看 PostgreSQL 日志并确保它没有抱怨检查点发生得太频繁。
max_wal_size
checkpoint_segments
log_checkpoints
当且仅当您不介意在导入过程中系统崩溃时将整个 PostgreSQL 集群(您的数据库和同一集群上的任何其他集群)丢失为灾难性损坏,您可以停止 Pg,设置fsync=off,启动 Pg,进行导入,然后(重要地)停止 Pg 并fsync=on再次设置。请参阅WAL 配置。 如果 PostgreSQL 安装上的任何数据库中已经存在您关心的任何数据,请不要这样做。 如果你设置了fsync=off,你也可以设置full_page_writes=off;同样,请记住在导入后重新打开它,以防止数据库损坏和数据丢失。请参阅Pg 手册中的非持久设置。
fsync=off
fsync=on
full_page_writes=off
您还应该考虑调整您的系统:
尽可能使用 优质SSD 进行存储。 具有可靠、受电源保护的回写高速缓存的优质 SSD 可让提交速度快得令人难以置信。当您遵循上述建议时,它们的用处不大——这减少了磁盘刷新/ fsync()s 的数量——但仍然可以提供很大的帮助。除非您不关心保存数据,否则不要使用没有适当电源故障保护的廉价 SSD。
fsync()
如果您将 RAID 5 或 RAID 6 用于直连存储,请立即停止。备份您的数据,将您的 RAID 阵列重组为 RAID 10,然后重试。RAID 5/6 对批量写入性能毫无希望——尽管具有大缓存的良好 RAID 控制器可以提供帮助。
如果您可以选择使用具有大电池支持的回写缓存的硬件 RAID 控制器,这可以真正提高具有大量提交的工作负载的写入性能。如果您使用带有 commit_delay 的异步提交,或者您在批量加载期间执行的大事务较少,则它没有多大帮助。
如果可能,将 WAL(pg_wal或pg_xlog旧版本)存储在单独的磁盘/磁盘阵列上。在同一个磁盘上使用单独的文件系统没有什么意义。人们经常选择为 WAL 使用 RAID1 对。同样,这对具有高提交率的系统影响更大,如果您使用未记录的表作为数据加载目标,它几乎没有影响。
pg_wal
pg_xlog
您可能还对优化 PostgreSQL 以进行快速测试感兴趣。