データベースにデータを初期投入するために、大量のテーブル挿入操作を行う必要がままあります。 本節では、この作業を効率良く行うためのちょっとした提言を示します。
複数回のINSERT
を実行するのであれば、自動コミットを無効にして最後に1回だけコミットしてください。
(普通のSQLでは、これはBEGIN
を開始時に、COMMIT
を最後に発行することを意味します。
クライアント用ライブラリの中にはこれを背後で実行するものもあります。
その場合は、要望通りにライブラリが行っているかどうかを確認しなければなりません。)
各挿入操作で個別にコミットすることを許すと、PostgreSQLは行を追加する度に多くの作業をしなければなりません。
1つのトランザクションですべての挿入を行うことによるもう1つの利点は、1つの行の挿入に失敗した場合、その時点までに挿入されたすべての行がロールバックされることです。
その結果、一部のみがロードされたデータの対処に困ることはありません。
COPY
の使用
単一コマンドですべての行をロードするために一連のINSERT
コマンドではなく、COPY
を使用してください。
COPY
コマンドは行を大量にロードすることに最適化されています。
このコマンドはINSERT
に比べ柔軟性に欠けていますが、大量のデータロードにおけるオーバーヘッドを大きく低減します。
COPY
コマンドでテーブルにデータを投入する場合、コマンドは1つなので、自動コミットを無効にする必要はありません。
COPY
を使用できない場合、準備されたINSERT
文をPREPARE
を使用して作成し、必要な回数だけ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
)を実行することで、確実にプランナがテーブルに関する最新の統計情報を持つことができます。
統計情報が存在しない、または古い場合、プランナは、そのテーブルに対する問い合わせの性能を損なわせる、お粗末な問い合わせ計画を選択する可能性があります。
自動バキュームデーモンが有効な場合、ANALYZE
が自動的に実行されます。
詳細は25.1.3および25.1.6を参照してください。
pg_dumpで生成されるダンプスクリプトは自動的に上のガイドラインのいくつかを適用します(すべてではありません)。 pg_dumpダンプをできる限り高速にリストアするには、手作業で更に数作業が必要です。 (これらは作成時に適用するものではなく、ダンプを復元する時に適用するものです。 psqlを使用してテキスト形式のダンプをロードする時とpg_dumpのアーカイブファイルからpg_restoreを使用してロードする時にも同じことが適用できます。)
デフォルトでは、pg_dumpはCOPY
を使用します。
スキーマとデータのダンプ全体を生成する場合、インデックスと外部キー制約を作成する前にデータをロードすることに注意してください。
ですので、この場合、ガイドラインのいくつかは自動的に行われます。
残された作業は以下のとおりです。
maintenance_work_mem
およびmax_wal_size
を適切な(つまり通常よりも大きな)値に設定します。
WALアーカイブ処理またはストリーミングレプリケーションを使用する場合は、リストア時にこれを無効にすることを検討してください。
このためにはダンプをロードする前にarchive_mode
をoff
に、wal_level
をminimal
に、max_wal_senders
をゼロに設定してください。
その後それらを正しい値に戻し、新規にベースバックアップを取ってください。
pg_dumpとpg_restoreで、並列ダンプとリストア方式を実験して、利用する並列なジョブの最適な数を見つけて下さい。
-j
オプションでダンプとリストアを並列に行なうのは逐次方式よりも大きく性能を向上させるでしょう。
ダンプ全体を単一トランザクションとしてリストアすべきかどうか検討してください。
このためにはpsqlまたはpg_restoreに-1
または--single-transaction
コマンドラインオプションを指定してください。
このモードを使用する場合、たとえ小さなエラーであっても、エラーがあればリストア全体がロールバックされます。
データ同士の関連性がどの程度あるかに依存しますが、手作業での整理の際には好まれるかと思います。さもなくばあまり勧めません。
単一トランザクションで実行し、WALアーカイブを無効にしている場合、COPY
コマンドは最も高速に行われます。
データベースサーバで複数のCPUが利用できるのであれば、pg_restoreの--jobs
オプションの利用を検討してください。
これによりデータのロードとインデックスの作成を同時に行うことができます。
この後でANALYZE
を実行してください。
データのみのダンプもCOPY
コマンドを使用しますが、インデックスの削除と再作成を行いません。
また、通常は外部キー制約を変更しません。
[14]
したがって、データのみのダンプをロードする時、上の技法を使用したければ自らインデックスと外部キーを削除、再作成しなければなりません。
データをロードする時にmax_wal_size
を増やすことも有用です。
しかし、maintenance_work_mem
を増やすことは考えないでください。
これは、後でインデックスと外部キーを手作業で再作成する時に行う方がよいでしょう。
また、実行した後でANALYZE
を行うことを忘れないでください。
詳細は25.1.3および25.1.6を参照してください。
[14]
--disable-triggers
オプションを使用して、外部キーを無効にさせることができます。
しかし、これは外部キー制約を遅らせるのではなく、除去することに注意してください。
そのため、これを使用すると不正なデータを挿入することができてしまいます。