受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

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 INDEXALTER 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。(相较于写 WAL,它们只需在最后执行一次fsync,就能以更小的代价保证崩溃安全。)

14.4.8. 事后运行ANALYZE #

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



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

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。