データベースへのデータ投入

PostgreSQL 9.1.1文書
前のページ | 巻戻し | 第 14章性能に関するヒント | 早送り | 次のページ

# 14.4. データベースへのデータ投入

データベースにデータを初期投入するために、大量のテーブル挿入操作を行う必要がままあります。 本節では、この作業を効率良く行うためのちょっとした提言を示します。

## 14.4.1. 自動コミットをオフにする

複数回の`INSERT`を実行するのであれば、自動コミットを無効にして最後に1回だけコミットしてください。 （普通のSQLでは、これは`BEGIN`を開始時に、`COMMIT`を最後に発行することを意味します。 クライアント用ライブラリの中にはこれを背後で実行するものもあります。 その場合は、要望通りにライブラリが行っているかどうかを確認しなければなりません。） 各挿入操作で個別にコミットすることを許すと、PostgreSQLは行を追加する度に多くの作業をしなければなりません。 1つのトランザクションですべての挿入を行うことによるもう1つの利点は、1つの行の挿入に失敗した場合、その時点までに挿入されたすべての行がロールバックされることです。 その結果、一部のみがロードされたデータの対処に困ることはありません。

## 14.4.2. `COPY`の使用

単一コマンドですべての行をロードするために一連の`INSERT`コマンドではなく、COPYを使用してください。 `COPY`コマンドは行を大量にロードすることに最適化されています。 このコマンドは`INSERT`に比べ柔軟性に欠けていますが、大量のデータロードにおけるオーバーヘッドを大きく低減します。 `COPY`コマンドでテーブルにデータを投入する場合、コマンドは1つなので、自動コミットを無効にする必要はありません。

`COPY`を使用できない場合、準備された`INSERT`文をPREPAREを使用して作成し、必要数回だけ`EXECUTE`を実行する方が良いでしょう。 これにより、繰り返し行われる`INSERT`の解析と計画作成分のオーバーヘッドを省くことになります。 この機能のための方法はインタフェースによって異なります。 このインタフェースの文書の"準備された文"を参照してください。

`COPY`を使用した大量の行のロードは、ほとんどすべての場合において、`INSERT`を使用するロードよりも高速です。 たとえ複数の挿入を単一トランザクションにまとめたとしても、またその際に`PREPARE`を使用したとしても、これは当てはまります。

`COPY`は、前もって行われる`CREATE TABLE`または`TRUNCATE`コマンドと同一トランザクションで行った場合に、最速です。 この場合、エラーが起きた場合に新しくロードされるデータを含むファイルがとにかく削除されますので、WALを書き出す必要がありません。 しかし、wal_levelが`minimal`に設定されている場合のみにこの方法は当てはまります。 この他の場合には、すべてのコマンドをWALに書き出さなければならないためです。

## 14.4.3. インデックスを削除する

新規に作成したテーブルをロードする時、最速の方法は、テーブルを作成し、`COPY`を使用した一括ロードを行い、そのテーブルに必要なインデックスを作成することです。 既存のデータに対するインデックスを作成する方が、各行がロードされる度に段階的に更新するよりも高速です。

既存のテーブルに大量のデータを追加しているのであれば、インデックスを削除し、テーブルをロード、その後にインデックスを再作成する方がよいかもしれません。 もちろん、他のユーザから見ると、インデックスが存在しない間データベースの性能は悪化します。 また、一意性インデックスを削除する前には熟考しなければなりません。 一意性制約によるエラー検査がその期間行われないからです。

## 14.4.4. 外部キー制約の削除

インデックスの場合と同様、外部キー制約は一行一行検査するよりも効率的に、"まとめて"検査することができます。 従って、外部キー制約を削除し、データをロード、そして、制約を再作成する方法は有用となることがあります。 繰り返しますが、データロードの速度と、制約が存在しない間のエラー検査がないという点とのトレードオフがあります。

外部キー制約をすでに持つテーブルにデータをロードする時、新しい行はそれぞれ(行の外部キー制約を検査するトリガを発行しますので)サーバの待機中トリガイベントのリスト内に項目を要求します。 数百万の行をロードすると、トリガイベントのキューが利用可能なメモリをオーバーフローさせてしまい、耐えられないほどのスワッピングが発生してしまう、最悪はそのコマンドが完全に失敗してしまう可能性があります。 したがって単に好ましいだけでなく、大量のデータをロードする時には外部キーを削除し再度適用することが必要かもしれません。 一時的な制約削除が受け入れられない場合に他に取り得る手段は、ロード操作をより小さなトランザクションに分割することだけかもしれません。

## 14.4.5. `maintenance_work_mem`を増やす

大規模なデータをロードする時maintenance_work_mem設定変数を一時的に増やすことで性能を向上させることができます。 これは、`CREATE INDEX`コマンドと`ALTER TABLE ADD FOREIGN KEY`の速度向上に役立ちます。 `COPY`自体には大して役立ちませんので、この助言は、上述の技法の片方または両方を使用している時にのみ有用です。

## 14.4.6. `checkpoint_segments`を増やす

大規模なデータをロードする時checkpoint_segments設定変数を一時的に増やすことで高速化することができます。 大量のデータをPostgreSQLにロードすることで、通常のチェックポイントの頻度（`checkpoint_timeout`設定変数により指定されます）よりも頻繁にチェックポイントが発生するためです。 チェックポイントが発生すると、すべてのダーティページ（ディスクに未書き込みの変更済みメモリページ）はディスクに吐き出されなければなりません。 大量のデータロードの際に一時的に`checkpoint_segments`を増加させることで、必要なチェックポイント数を減らすことができます。

## 14.4.7. WALアーカイブ処理とストリーミングレプリケーションの無効化

大量のデータをWALアーカイブ処理またはストリーミングレプリケーションを使用するインストレーションにロードする時、増加する大量のWALデータを処理するより、ロードが完了した後に新しくベースバックアップを取る方が高速です。 ロード中のWALログの増加を防ぐためには、wal_levelを`minimal`に、archive_modeを`off`に、max_wal_sendersをゼロに設定することにより、アーカイブ処理とストリーミングレプリケーションを無効にしてください。 しかし、これらの変数を変更するにはサーバの再起動が必要な点に注意してください。

こうすると、WALデータを処理する保管処理またはWAL送信処理にかかる時間がかからないことの他に、実際のところ、特定のコマンドをより高速にします。 `wal_level`が`minimal`の場合、これらのコマンドではWALへの書き出しは全く予定されないためです。 （これらは最後に`fsync`を実行することで、WALへの書き込みより安価にクラッシュした場合の安全性を保証することができます。） これは以下のコマンドで当てはまります。

- `CREATE TABLE AS SELECT`

- `CREATE INDEX` (または`ALTER TABLE ADD PRIMARY KEY`などの亜種）

- `ALTER TABLE SET TABLESPACE`

- `CLUSTER`

- 同一トランザクションで前もって対象テーブルが作成された、あるいは、消去された場合の`COPY FROM`

## 14.4.8. 最後に`ANALYZE`を実行

テーブル内のデータ分布を大きく変更した時は毎回、ANALYZEを実行することを強く勧めます。 これは、テーブルに大量のデータをまとめてロードする場合も含まれます。 `ANALYZE`（または`VACUUM ANALYZE`）を実行することで、確実にプランナがテーブルに関する最新の統計情報を持つことができます。 統計情報が存在しない、または古い場合、プランナは、そのテーブルに対する問い合わせの性能を損なわせる、お粗末な問い合わせ計画を選択する可能性があります。 自動バキュームデーモンが有効な場合、`ANALYZE`が自動的に実行されます。 詳細は項23.1.3および項23.1.5を参照してください。

## 14.4.9. pg_dumpに関するいくつかの注意

pg_dumpで生成されるダンプスクリプトは自動的に上のガイドラインのいくつかを適用します（すべてではありません）。 pg_dumpダンプをできる限り高速にリロードするには、手作業で更に数作業が必要です。 （これらは作成時に適用するものではなく、ダンプを復元する時に適用するものです。 psqlを使用してテキスト形式のダンプをロードする時とpg_dumpのアーカイブファイルからpg_restoreを使用してロードする時にも同じことが適用できます。）

デフォルトでは、pg_dumpは`COPY`を使用します。 スキーマとデータのダンプ全体を生成する場合、インデックスと外部キー制約を作成する前にデータをロードすることに注意してください。 ですので、この場合、ガイドラインのいくつかは自動的に行われます。 残された作業は以下のとおりです。

- `maintenance_work_mem`および`checkpoint_segments`を適切な（つまり通常よりも大きな）値に設定します。

- WALアーカイブ処理またはストリーミングレプリケーションを使用する場合は、リストア時にこれを無効にすることを検討してください。 このためにはダンプをロードする前に`archive_mode`を`off`に、`wal_level`を`minimal`に、`max_wal_senders`をゼロに設定してください。 その後それらを正しい値に戻し、新規にベースバックアップを取ってください。

- ダンプ全体を単一トランザクションとしてリストアすべきかどうか検討してください。 このためにはpsqlまたはpg_restoreに`-1`または`--single-transaction`コマンドラインオプションを指定してください。 このモードを使用する場合、たとえ小さなエラーであっても、エラーがあればリストア全体がロールバックされます。 データ同士の関連性がどの程度あるかに依存しますが、手作業での整理の際には好まれるかと思います。さもなくばあまり勧めません。 単一トランザクションで実行し、WALアーカイブを無効にしている場合、`COPY`コマンドは最も高速に行われます。

- データベースサーバで複数のCPUが利用できるのであれば、pg_restoreの`--jobs`オプションの利用を検討してください。 これによりデータのロードとインデックスの作成を同時に行うことができます。

- この後で`ANALYZE`を実行してください。

データのみのダンプも`COPY`コマンドを使用しますが、インデックスの削除と再作成を行いません。 また、通常は外部キー制約を変更しません。 [1] したがって、データのみのダンプをロードする時、上の技法を使用したければ自らインデックスと外部キーを削除、再作成しなければなりません。 データをロードする時に`checkpoint_segments`を増やすことも有用です。 しかし、`maintenance_work_mem`を増やすことは考えないでください。 これは、後でインデックスと外部キーを手作業で再作成する時に行う方がよいでしょう。 また、実行した後で`ANALYZE`を行うことを忘れないでください。 詳細は項23.1.3および項23.1.5を参照してください。

### 注意

[1] | `--disable-triggers`オプションを使用して、外部キーを無効にさせることができます。 しかし、これは外部キー制約を遅らせるのではなく、除去することに注意してください。 そのため、これを使用すると不正なデータを挿入することができてしまいます。

前のページ | ホーム | 次のページ
明示的な`JOIN`句でプランナを制御する | 上に戻る | 永続性がない設定