↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 13 / 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
历史版本。 PostgreSQL 13 已结束支持。 2025-11-13. 请参阅 当前版本手册.

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 TABLE 或 TRUNCATE 命令处于同一事务中时,速度最快。在这种情况下,不需要写 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_level 为 minimal,并且当前子事务(或顶级事务)创建了或截断了它们所修改的表或索引,那么这些命令就完全不需要写 WAL。(相较于写 WAL,它们只需在最后执行一次 fsync,就能以更小的代价保证崩溃安全。)

14.4.8. 事后运行 ANALYZE #

每当显著改变了表中数据的分布,都强烈建议运行 ANALYZE。这也包括向表中批量装载大量数据。运行 ANALYZE(或 VACUUM ANALYZE)可以确保规划器掌握该表的最新统计信息。如果没有统计信息,或者统计信息已经过时,规划器在生成查询计划时就可能做出糟糕决定,从而导致相关表性能不佳。注意,如果启用了自动清理守护进程,它可能会自动运行 ANALYZE;详见第 24.1.3 节和第 24.1.6 节。

14.4.9. 关于 pg_dump 的一些注记 #

由 pg_dump 生成的转储脚本会自动应用上面若干条指导原则,但并非全部。若要尽可能快速地还原 pg_dump 的转储,仍需手动做一些额外操作。(注意,这些要点适用于还原转储,而不是创建转储。无论是使用 psql 加载文本转储,还是使用 pg_restore 从 pg_dump 归档文件加载,相关要点都是一样的。)

默认情况下,pg_dump 使用 COPY;而当它生成完整的模式加数据转储时,也会小心地先装载数据,再创建索引和外键。因此在这种情况下,上述若干指导原则已经被自动处理。剩下需要做的是:

  • 为 maintenance_work_mem 和 max_wal_size 设置适当的(即比正常值大的)值。

  • 如果使用 WAL 归档或流复制,可以考虑在恢复期间禁用它们。为此,请在载入转储之前将 archive_mode 设为 off、将 wal_level 设为 minimal,并将 max_wal_senders 设为零。恢复完成后,再把这些设置改回正确的值,并重新做一次基础备份。

  • 试验 pg_dump 和 pg_restore 的并行转储与恢复模式,找出最优的并发任务数量。通过 -j 选项进行并行转储和恢复,通常会比串行模式获得高得多的性能。

  • 考虑是否应当把整个转储作为单个事务来恢复。要这样做,请把 -1 或 --single-transaction 命令行选项传给 psql 或 pg_restore。使用这种模式时,即使是很小的错误也会回滚整个恢复过程,可能丢掉数小时的处理成果。视数据之间的关联程度而定,这种做法未必一定比手工清理更可取。如果使用单个事务并关闭 WAL 归档,COPY 命令会运行得最快。

  • 如果在数据库服务器上有多个 CPU 可用,可以考虑使用 pg_restore 的 --jobs 选项。这允许并行数据载入和索引创建。

  • 之后运行 ANALYZE。

仅包含数据的转储仍然会使用 COPY,但它不会删除或重建索引,通常也不会处理外键。[13] 因此,在装载纯数据转储时,如果想采用这些技术,就需要自行负责删除并重建索引与外键。装载数据期间增大 max_wal_size 仍然有益,但没有必要同时增大 maintenance_work_mem;后者更适合留到之后手工重建索引和外键时再调大。完成后也别忘了执行 ANALYZE;详见第 24.1.3 节和第 24.1.6 节。



[13] 可以通过使用 --disable-triggers 选项达到禁用外键的效果 — 但要注意,这样做是取消外键验证,而不仅仅是推迟它。因此如果使用该选项,就有可能插入坏数据。

报告文档问题

阅读 上游文档. 反馈更正前请先核对 当前版本手册.