2024年9月26日: PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不受支持的版本:11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

14.4. 填充数据库 #

首次填充数据库时,可能需要插入大量数据。本节包含一些关于如何使此过程尽可能高效的建议。

14.4.1. 禁用自动提交 #

当使用多个 INSERT 时,关闭自动提交,并在最后执行一次提交。(在纯 SQL 中,这意味着在开始时发出 BEGIN,在结束时发出 COMMIT。一些客户端库可能会在后台执行此操作,在这种情况下,您需要确保库在您希望执行时执行此操作。)如果您允许每个插入单独提交,PostgreSQL 会为添加的每一行执行大量工作。执行所有插入操作的一个事务的另一个好处是,如果插入一行失败,则回滚到该点之前插入的所有行的插入,因此您不会遇到部分加载的数据。

14.4.2. 使用 COPY #

使用 COPY 在一个命令中加载所有行,而不是使用一系列 INSERT 命令。COPY 命令针对加载大量行进行了优化;它不如 INSERT 灵活,但对于大型数据加载产生的开销要少得多。由于 COPY 是单个命令,因此如果您使用此方法填充表,则无需禁用自动提交。

如果您无法使用 COPY,则可能有助于使用 PREPARE 创建一个准备好的 INSERT 语句,然后根据需要多次使用 EXECUTE。这避免了重复解析和计划 INSERT 的一些开销。不同的接口以不同的方式提供此功能;在接口文档中查找“预备语句”。

请注意,使用 COPY 加载大量行几乎总是比使用 INSERT 快,即使使用了 PREPARE 并且多个插入被批处理到单个事务中。

COPY 在与较早的 CREATE TABLETRUNCATE 命令位于同一事务中时速度最快。在这种情况下,不需要写入 WAL,因为在发生错误时,包含新加载数据的文件将被删除。但是,此考虑因素仅适用于 wal_levelminimal 的情况,因为否则所有命令都必须写入 WAL。

14.4.3. 删除索引 #

如果您正在加载一个新创建的表,最快的的方法是创建表,使用 COPY 批量加载表的数据,然后创建表所需的任何索引。在预先存在的数据上创建索引比在加载每一行时增量更新它更快。

如果您正在向现有表添加大量数据,则删除索引、加载表,然后重新创建索引可能会有所帮助。当然,在索引丢失期间,其他用户的数据库性能可能会受到影响。在删除唯一索引之前,也应该三思而后行,因为在索引丢失时,唯一约束提供的错误检查将丢失。

14.4.4. 删除外键约束 #

与索引一样,外键约束可以比逐行更有效地“批量检查。因此,删除外键约束、加载数据并重新创建约束可能很有用。同样,在数据加载速度和约束丢失时错误检查的损失之间存在权衡。

此外,当您将数据加载到具有现有外键约束的表中时,每一行都需要在服务器的挂起触发器事件列表中添加一个条目(因为触发器触发检查行的外键约束)。加载数百万行可能会导致触发器事件队列溢出可用内存,从而导致无法忍受的交换甚至命令彻底失败。因此,在加载大量数据时,删除和重新应用外键可能是必要的,而不仅仅是理想的。如果暂时删除约束不可接受,唯一其他选择可能是将加载操作拆分为较小的事务。

14.4.5. 增加 maintenance_work_mem #

在加载大量数据时暂时增加 maintenance_work_mem 配置变量会导致性能提升。这将有助于加快 CREATE INDEX 命令和 ALTER TABLE ADD FOREIGN KEY 命令的速度。它对 COPY 本身没有太大作用,因此此建议仅在您使用上述一项或两项技术时才有用。

14.4.6. 增加 max_wal_size #

暂时增加 max_wal_size 配置变量也可以使大型数据加载更快。这是因为将大量数据加载到 PostgreSQL 中会导致检查点比正常检查点频率(由 checkpoint_timeout 配置变量指定)更频繁地发生。每当发生检查点时,所有脏页都必须刷新到磁盘。通过在批量数据加载期间暂时增加 max_wal_size,可以减少所需的检查点数量。

14.4.7. 禁用 WAL 归档和流式复制 #

当将大量数据加载到使用 WAL 归档或流式复制的安装中时,在加载完成后进行新的基本备份可能比处理大量增量 WAL 数据更快。为了防止在加载期间进行增量 WAL 记录,请禁用归档和流式复制,方法是将 wal_level 设置为 minimalarchive_mode 设置为 off,并将 max_wal_senders 设置为零。但请注意,更改这些设置需要服务器重新启动,并且使在此之前拍摄的任何基本备份都无法用于归档恢复和备用服务器,这可能导致数据丢失。

除了避免归档程序或 WAL 发送器处理 WAL 数据的时间外,这样做实际上会使某些命令更快,因为如果 wal_levelminimal 并且当前子事务(或顶级事务)创建或截断了它们更改的表或索引,则它们根本不需要写入 WAL。(它们可以通过在最后执行 fsync 来更廉价地保证崩溃安全性,而不是写入 WAL。)

14.4.8. 之后运行 ANALYZE #

每当您显着更改了表中数据的分布时,强烈建议运行 ANALYZE。这包括将大量数据批量加载到表中。运行 ANALYZE(或 VACUUM ANALYZE)可确保计划程序拥有关于表的最新统计信息。如果没有统计信息或统计信息已过时,计划程序可能会在查询计划期间做出错误的决定,从而导致对任何具有不准确或不存在统计信息的表的性能下降。请注意,如果启用了自动 vacuum 守护程序,它可能会自动运行 ANALYZE;有关更多信息,请参见第 24.1.3 节第 24.1.6 节

14.4.9. 关于 pg_dump 的一些说明 #

pg_dump 生成的转储脚本会自动应用上述指南中的几个(但不是全部)。为了尽快恢复 pg_dump 转储,您需要手动执行一些额外操作。(请注意,这些要点适用于恢复转储,而不是创建转储。无论使用 psql 加载文本转储还是使用 pg_restorepg_dump 归档文件加载,这些要点都适用。)

默认情况下,pg_dump 使用 COPY 命令,并且在生成完整的模式和数据转储时,会小心地在创建索引和外键之前加载数据。因此,在这种情况下,一些准则会自动处理。您需要做的是

  • 设置适当的(即大于正常值)maintenance_work_memmax_wal_size 值。

  • 如果使用 WAL 归档或流复制,请考虑在恢复期间禁用它们。为此,在加载转储之前,将 archive_mode 设置为 offwal_level 设置为 minimal,并将 max_wal_senders 设置为零。之后,将它们恢复到正确的值并进行新的基础备份。

  • 尝试 pg_dumppg_restore 的并行转储和恢复模式,并找到要使用的最佳并发作业数。通过 -j 选项并行转储和恢复应该可以提供比串行模式显著更高的性能。

  • 考虑是否应将整个转储作为单个事务恢复。为此,将 -1--single-transaction 命令行选项传递给 psqlpg_restore。使用此模式时,即使是最小的错误也会回滚整个恢复,可能会丢弃数小时的处理过程。根据数据的关联程度,这可能看起来优于手动清理,也可能不是。如果您使用单个事务并关闭了 WAL 归档,则 COPY 命令将运行最快。

  • 如果数据库服务器中有多个 CPU 可用,请考虑使用 pg_restore--jobs 选项。这允许并发数据加载和索引创建。

  • 之后运行 ANALYZE

仅数据转储仍将使用 COPY,但它不会删除或重新创建索引,并且通常不会触及外键。[14] 因此,在加载仅数据转储时,您可以根据需要删除和重新创建索引和外键。在加载数据时增加 max_wal_size 仍然很有用,但不要费心增加 maintenance_work_mem;相反,您可以在之后手动重新创建索引和外键时这样做。并且不要忘记在完成后运行 ANALYZE;有关更多信息,请参见第 24.1.3 节第 24.1.6 节



[14] 您可以通过使用 --disable-triggers 选项来获得禁用外键的效果——但请意识到,这消除了外键验证,而不是仅仅推迟它,因此如果您使用它,则可能会插入错误数据。

提交更正

如果您在文档中看到任何不正确的内容,与您对特定功能的体验不符,或者需要进一步澄清,请使用此表单 报告文档问题。