第一次填充数据库时可能需要插入大量的数据。本节包含一些如何让这个过程尽可能高效的建议。
在使用多个INSERT时,关闭自动提交并且只在最后做一次提交。
(在普通 SQL 中,这意味着在开始发出BEGIN并且在结束时发出COMMIT。
某些客户端库可能背着你就做了这些,在这种情况下你需要确保在你需要做这些时该库确实帮你做了。)
如果你允许每一个插入都被独立地提交,PostgreSQL要为每一个被增加的行做很多工作。
在一个事务中做所有插入的一个额外好处是:如果一个行的插入失败则所有之前插入的行都会被回滚,
这样你不会被卡在部分加载的数据中。
COPY #
使用COPY在一条命令中加载所有记录,而不是一系列INSERT命令。
COPY命令是为加载大量行而优化过的;它没有INSERT那么灵活,
但是在大量数据加载时导致的开销也更少。因为COPY是单条命令,
因此使用这种方法填充表时无须关闭自动提交。
如果你不能使用COPY,那么使用PREPARE来创建一个预备INSERT语句也有所帮助,
然后根据需要使用EXECUTE多次。这样就避免了重复解析和规划INSERT的开销。
不同接口以不同的方式提供该功能,可参阅接口文档中的“预备语句”。
请注意,在加载大量行时,使用COPY几乎总是比使用INSERT快,
即使使用了PREPARE并且把多个插入成批放入一个单一事务。
在同一个事务中,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)保证规划器有表的最新统计信息。 如果没有统计数据或者统计数据过时,那么规划器在查询规划时可能做出很差劲决定,导致在任意表上的性能低下。需要注意的是,如果启用了 autovacuum 守护进程,它可能会自动运行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 节。