13.4. 向数据库中添加记录

我们第一次填充数据库时可能需要做大量的表插入。 下面是一些建议,可以尽可能高效地处理这些事情。

13.4.1. 关闭自动提交

关闭自动提交,并且只在每次(数据拷贝)结束的时候做一次提交。 (在纯 SQL 里,这就意味着在开始的时候发出 BEGIN, 并且在结束的时候执行 COMMIT。有些客户端的库可能背着你干这些事情, 这种情况下你必须确信只有在你要那些库干这些事情的时候它才做。) 如果你允许每个插入都独立地提交,那么 PostgreSQL 会为所增加的每行记录做大量的处理。 在一个事务里完成所有插入的动作的最大的好处就是,如果有一条记录插入失败, 那么,到该点为止的所有已插入记录都将被回滚,这样你就不会很难受地面对一个只装载了一部分数据的表。

13.4.2. 使用 COPY

使用 COPY 在一条命令里装载所有记录, 而不是一连串的INSERT命令。COPY 命令是为装载数量巨大的数据行优化过的; 它没 INSERT 那么灵活,但是在大量装载数据的情况下,导致的过荷也少很多。 因为 COPY 是单条命令,因此填充表的时候就没有必要关闭自动提交了。

如果你不能使用 COPY,那么 使用 PREPARE 来创建一个准备好的 INSERT, 然后使用 EXECUTE 多次效率更高。 这样就避免了重复分析和规划 INSERT 的开销。

请注意,在装载大量数据行的时候,COPY 几乎总是比 INSERT 快, 即使使用了 PREPARE 并且把多个 INSERT 命令绑在一个事务中也是这样的。

13.4.3. 删除索引

如果你正在装载一个新创建的表,最快的方法是创建表, 用COPY批量装载,然后创建表需要的任何索引。 在已存在数据的表上创建索引要比递增地更新所装载的每一行记录要快。

如果你对现有表增加大量的数据,可能先删除索引,装载表,然后重新创建索引更快些。 当然,在缺少索引的期间,其他数据库用户的数据库性能将有负面的影响。 并且我们在删除唯一索引之前还需要仔细考虑清楚,因为唯一约束 提供的错误检查在缺少索引的时候会消失.

13.4.4. 删除外键约束

和索引一样,"批量地"检查外键约束比一行行检查更高效。 因此,也许我们先删除外键约束,装载数据,然后重建约束会更高效。 同样,装载数据和缺少约束而失去错误检查之间也有一个平衡。

13.4.5. 增大 maintenance_work_mem

在装载大量的数据的时候,临时增大 maintenance_work_mem 配置变量可以改进性能。 这个参数也可以帮助加速 CREATE INDEX 命令和 ALTER TABLE ADD FOREIGN KEY 命令。它不会对 COPY 本身有多大作用,所以这个建议只有在你使用上面的两个技巧中的一个或两个才有效。

13.4.6. 增大 checkpoint_segments

临时增大 checkpoint_segments 配置变量也可以让大量数据装载得更快。 这是因为向 PostgreSQL 里面装载大量的数据可以导致检查点操作 (由配置变量 checkpoint_timeout 声明) 比平常更加频繁发生。在发生一个检查点的时候,所有脏数据都必须刷新到磁盘上。 通过在大量数据装载的时候临时增加 checkpoint_segments, 所要求的检查点的数目可以减少。

13.4.7. 事后运行ANALYZE

不管甚么时候,如果你在增加或者更新了大量数据之后, 运行 ANALYZE 都是个好习惯。 运行 ANALYZE(或者 VACUUM ANALYZE) 可以保证规划器有最新的表的数据的统计。 如果没有统计数据或者统计数据太陈旧,那么规划器可能选择很差劲的查询规划,导致检索你的表的查询性能的恶化。

13.4.8. pg_dump 的一些事项

pg_dump 生成的转储脚本自动使用上面的若干个技巧, 但不是全部。要尽可能快地装载 pg_dump 转储, 我们需要手工做几个事情。(请注意,这些要点适用于恢复一个转储, 而不是创建一个转储的时候。同样的要点也适用于使用 pg_restorepg_dump 归档文件装载数据的时候。)

缺省的时候,pg_dump 使用 COPY, 在它生成一个完整的模式和数据的转储的时候,它会很小心地先装载数据,然后创建索引和外键。 因此,在这个情况下,头几条技巧是自动处理的。你需要做的只是在装载转储脚本之前设置合适的(也就是说,比正常状况要大的) maintenance_work_mem 值和 checkpoint_segments 值, 然后在装载完成之后运行 ANALYZE

只保存数据的转储仍然会使用 COPY,但是它不会删除或者重建索引, 并且它不会自动修改外键。 [1] 因此,在装载只有数据的转储的时候,是否使用删除以及重建索引和外键等技巧完全取决于你。 装载数据的时候,增大 checkpoint_segments 仍然是有用的, 但是增大 maintenance_work_mem 就没什么必要了; 你只是应该在事后手工创建索引和外键的事后增大它。

Notes

[1]

你可以通过使用 -X disable-triggers 选项的方法获取关闭外键的效果 — 不过要意识到这么做是消除,而不只是推迟违反外键约束,因此如果你使用这个选项, 是有可能插入坏数据的。