在系统初始化导入或迁移海量历史数据时,如果逐条执行普通的 INSERT,性能往往难以接受。以下工程建议能够将批量加载速度提升数倍至数十倍:
在单次事务(BEGIN ... COMMIT)中打包执行成千上万条插入,避免为每条单独的行记录反复触发磁盘 fsync 刷盘与事务提交流程。
对于向新建空表中导入数据,最佳实践是先建好表结构(不建索引),使用 COPY 全量装载完数据,最后再统一执行 CREATE INDEX。在既有存量数据上一次性批量构建 B 树索引的速度,远快于在每插入单行时逐行动态维护索引树。对于存量大表导入,若写入量极大,同样可以考虑临时删除非主键索引并在导入后重建。
当外键约束存在时,每次写入都需要向父表执行探针检索并生成待决触发器事件,导入数亿行数据极易挤爆内存队列。在大批量迁移时,建议先临时删除外键约束,完成高速导入后再统一重新添加外键约束。
maintenance_work_mem #在批量构建索引或添加外键之前,临时在会话中大幅调大 maintenance_work_mem(如分配数个 GB 内存),能够使排序与索引构建尽可能在高速内存中单趟完成,避免溢出到磁盘临时文件。
max_wal_size #海量数据写入会以极高速度产生巨大的 WAL 日志,如果 max_wal_size 过小,会导致系统极其频繁地被迫提前触发物理检查点刷盘,引发严重的 I/O 拥塞。在批量装载期间临时调大该参数可大幅减少检查点频次。
在向启用了 WAL 归档或流复制的集群中加载大量数据时,在加载完成后重新进行一次完整的基础备份,往往比在加载期间产生并传输海量增量 WAL 日志要快得多。此时可通过将 wal_level 设为 minimal、archive_mode 设为 off、max_wal_senders 设为零来关闭归档与流复制。注意修改这些参数需要重启服务,并在加载完成后立即进行全新基础备份。
将 wal_level 设为 minimal 还能使在当前事务中新建或截断的表完全免除 WAL 写入,系统仅在事务结束时调用 fsync 即可保证崩溃安全,大幅提升装载吞吐。
pg_dump 生成的转储脚本默认自动应用了上述若干准则(例如默认采用 COPY,并在恢复全量架构与数据时先导数据后建索引和外键)。为了以最快速度完成恢复,建议手动执行以下优化:
为 maintenance_work_mem 和 max_wal_size 分配比平时更大的数值。
若使用 WAL 归档或流复制,在恢复期间考虑将其关闭(将 archive_mode 设为 off,wal_level 设为 minimal,max_wal_senders 设为 0),恢复完成后恢复原值并做全量备份。
利用 pg_dump 和 pg_restore 的并行模式(-j 选项),指定并发作业数以充分利用多核资源。
考虑是否将整个转储作为单一事务恢复(psql 或 pg_restore 的 --single-transaction 选项)。