在首次填充数据库时,可能需要插入大量数据。本节包含一些关于如何使此过程尽可能高效的建议。
当使用多个 INSERT
时,关闭自动提交并在最后只提交一次。(在普通 SQL 中,这意味着在开始时发出 BEGIN
,在结束时发出 COMMIT
。某些客户端库可能会在您不知情的情况下执行此操作,在这种情况下,您需要确保该库在您希望它完成时完成。)如果您允许每次插入单独提交,则 PostgreSQL 对于添加的每一行都会执行大量工作。在一个事务中执行所有插入的另一个好处是,如果插入一行失败,则会回滚直到该点插入的所有行,因此您不会陷入部分加载的数据。
COPY
#使用 COPY
在一个命令中加载所有行,而不是使用一系列 INSERT
命令。COPY
命令针对加载大量行进行了优化;它不如 INSERT
灵活,但对于大型数据加载产生的开销要少得多。由于 COPY
是一个单独的命令,如果您使用此方法填充表,则无需禁用自动提交。
如果您不能使用 COPY
,则可以使用 PREPARE
创建预备的 INSERT
语句,然后根据需要多次使用 EXECUTE
。这避免了重复解析和规划 INSERT
的一些开销。不同的接口以不同的方式提供此功能;在接口文档中查找 “预备语句”。
请注意,即使使用 PREPARE
并且将多个插入批处理到单个事务中,使用 COPY
加载大量行几乎总是比使用 INSERT
快。
COPY
在与早期的 CREATE TABLE
或 TRUNCATE
命令在同一事务中使用时最快。在这种情况下,不需要写入 WAL,因为如果发生错误,包含新加载数据的文件无论如何都将被删除。但是,此考虑仅适用于 wal_level 为 minimal
的情况,因为所有命令都必须写入 WAL。
如果您要加载新创建的表,最快的方法是创建表,使用 COPY
批量加载表的数据,然后创建表所需的任何索引。在预先存在的数据上创建索引比在加载每一行时增量更新它要快。
如果要向现有表中添加大量数据,则删除索引、加载表,然后重新创建索引可能是一个好方法。当然,在索引丢失期间,其他用户的数据库性能可能会受到影响。在删除唯一索引之前也应该三思而后行,因为在索引丢失时,唯一约束提供的错误检查将丢失。
与索引一样,外键约束可以比逐行更有效地 “批量” 检查。因此,删除外键约束、加载数据和重新创建约束可能很有用。同样,在数据加载速度和约束丢失期间的错误检查之间需要权衡。
此外,当您将数据加载到具有现有外键约束的表中时,每个新行都需要在服务器的待处理触发器事件列表中添加一个条目(因为它是触发器的触发来检查该行的外键约束)。加载数百万行可能会导致触发器事件队列溢出可用内存,从而导致无法容忍的交换甚至命令的完全失败。因此,在加载大量数据时,删除和重新应用外键可能 是必要的,而不仅仅是期望的。如果暂时删除约束是不可接受的,那么唯一的其他方法可能是将加载操作拆分为较小的事务。
maintenance_work_mem
#在加载大量数据时,临时增加 maintenance_work_mem 配置变量可以提高性能。这将有助于加速 CREATE INDEX
命令和 ALTER TABLE ADD FOREIGN KEY
命令。它对 COPY
本身没有太大作用,因此此建议仅在您使用上述一种或两种技术时才有用。
max_wal_size
#临时增加 max_wal_size 配置变量也可以使大型数据加载更快。这是因为将大量数据加载到 PostgreSQL 中将导致检查点的发生频率高于正常检查点频率(由 checkpoint_timeout
配置变量指定)。每当发生检查点时,所有脏页都必须刷新到磁盘。通过在批量数据加载期间临时增加 max_wal_size
,可以减少所需的检查点数。
当将大量数据加载到使用 WAL 归档或流复制的安装中时,在加载完成后获取新的基本备份可能比处理大量增量 WAL 数据更快。为了防止在加载时进行增量 WAL 日志记录,请通过将 wal_level 设置为 minimal
,将 archive_mode 设置为 off
,以及将 max_wal_senders 设置为零来禁用归档和流复制。但是请注意,更改这些设置需要服务器重新启动,并且使之前拍摄的任何基本备份都无法用于归档恢复和备用服务器,这可能会导致数据丢失。
除了避免归档器或 WAL 发送器处理 WAL 数据的时间外,这样做实际上会使某些命令更快,因为如果 wal_level
是 minimal
并且当前子事务(或顶级事务)创建或截断了他们更改的表或索引,它们根本不需要写入 WAL。(通过在末尾执行 fsync
而不是写入 WAL,它们可以更便宜地保证崩溃安全性。)
ANALYZE
#每当您显著更改了表中数据的分布时,强烈建议运行 ANALYZE
。这包括将大量数据批量加载到表中。运行 ANALYZE
(或 VACUUM ANALYZE
) 可确保规划器拥有关于表的最新统计信息。如果没有统计信息或统计信息过时,规划器在查询规划期间可能会做出错误的决策,导致任何具有不准确或不存在统计信息的表性能不佳。请注意,如果启用了自动清理守护进程,它可能会自动运行 ANALYZE
;有关更多信息,请参阅第 24.1.3 节和第 24.1.6 节。
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
,但它不会删除或重新创建索引,并且通常不会触及外键。[14] 因此,在加载仅数据转储时,如果要使用这些技术,则由您来删除和重新创建索引和外键。 在加载数据时增加 max_wal_size
仍然有用,但不要费心增加 maintenance_work_mem
;相反,您将在之后手动重新创建索引和外键时执行此操作。并且不要忘记在完成后运行 ANALYZE
;有关更多信息,请参阅第 24.1.3 节和第 24.1.6 节。
如果您在文档中发现任何不正确的内容,与您使用特定功能的经验不符,或者需要进一步澄清的内容,请使用此表单报告文档问题。