2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 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_level 设置为 minimal 时,此考虑才适用,因为否则所有命令都必须写入 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 设置为 minimal,将 archive_mode 设置为 off,并将 max_wal_senders 设置为零来禁用归档和流复制。但请注意,更改这些设置需要服务器重启,并且会使之前获取的任何基本备份无法用于归档恢复和备用服务器,这可能会导致数据丢失。

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

14.4.8. 之后运行 ANALYZE #

每当您显著更改了表内数据的分布时,强烈建议运行 ANALYZE。这包括将大量数据批量加载到表中。运行 ANALYZE(或 VACUUM ANALYZE)可确保规划器具有关于表的最新统计信息。如果没有统计信息或统计信息过时,规划器在查询规划期间可能会做出错误的决定,导致具有不准确或不存在统计信息的任何表的性能下降。请注意,如果启用了 autovacuum 守护程序,它可能会自动运行 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 设置为 off,将 wal_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 选项获得禁用外键的效果 — 但请注意,这会消除外键验证,而不是仅仅推迟它,所以如果您使用它,就有可能插入错误的数据。

提交更正

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