PostgreSQLデータベースは、単一または最小限のステップで大量のデータをインポートする必要がある場合があります。これは一般にバルクデータインポートとして知られており、データソースは通常1つ以上の大きなファイルです。このプロセスは、許容できないほど遅くなる場合があります。
このようなパフォーマンスの低下には多くの理由があります。インデックス、トリガー、外部キー、GUID主キー、または先行書き込みログ(WAL)でさえ、すべて遅延を引き起こす可能性があります。
この記事では、PostgreSQLデータベースにデータを一括インポートするためのいくつかのベストプラクティスのヒントについて説明します。ただし、これらのヒントのいずれも効率的な解決策にならない場合があります。適用する前に、どの方法の長所と短所も検討することをお勧めします。
ヒント1:ターゲットテーブルをログなしモードに変更する
PostgreSQL 9.5以降の場合、ターゲットテーブルを最初にUNLOGGEDに変更し、データがロードされたらLOGGEDに戻すことができます。
ALTER TABLE <target table> SET UNLOGGED
<bulk data insert operations…>
ALTER TABLE <target table> LOGGED
UNLOGGEDモードは、PostgreSQLがテーブル書き込み操作を先行書き込みログ(WAL)に送信しないようにします。これにより、ロードプロセスが大幅に高速化されます。ただし、操作はログに記録されないため、ロード中にクラッシュまたはクリーンでないサーバーのシャットダウンが発生した場合、データを回復することはできません。 PostgreSQLは、再起動すると、ログに記録されていないテーブルを自動的に切り捨てます。
また、ログに記録されていないテーブルはスタンバイサーバーに複製されません。このような場合、既存のレプリケーションをロード前に削除し、ロード後に再作成する必要があります。プライマリノードのデータ量とスタンバイの数によっては、レプリケーションを再作成する時間が非常に長くなる可能性があり、高可用性要件では受け入れられない場合があります。
ログに記録されていないテーブルにデータを一括挿入するには、次のベストプラクティスをお勧めします。
- ログなしモードに変更する前に、テーブルとデータのバックアップを作成する
- データの読み込みが完了したら、スタンバイサーバーへのレプリケーションを再作成します
- 簡単に再入力できるテーブル(大きなルックアップテーブルやディメンションテーブルなど)にログに記録されていない一括挿入を使用する
ヒント2:インデックスを削除して再作成する
既存のインデックスは、バルクデータ挿入中に大幅な遅延を引き起こす可能性があります。これは、各行が追加されるたびに、対応するインデックスエントリも更新する必要があるためです。
一括挿入を開始する前に、可能な場合はターゲットテーブルにインデックスを削除し、ロードが完了したらインデックスを再作成することをお勧めします。繰り返しになりますが、大きなテーブルにインデックスを作成するには時間がかかる場合がありますが、通常、ロード中にインデックスを更新するよりも高速です。
DROP INDEX <index_name1>, <index_name2> … <index_name_n>
<bulk data insert operations…>
CREATE INDEX <index_name> ON <target_table>(column1, …,column n)
maintenance_work_memを一時的に増やすことは価値があるかもしれません インデックスを作成する直前の構成パラメーター。作業メモリーの増加は、インデックスの作成を高速化するのに役立ちます。
安全にプレイするためのもう1つのオプションは、既存のデータとインデックスを使用して同じデータベースにターゲットテーブルのコピーを作成することです。次に、この新しくコピーされたテーブルは、インデックスの削除と再作成、またはインデックスの動的更新の両方のシナリオで一括挿入を使用してテストできます。ライブテーブルでは、パフォーマンスを向上させる方法に従うことができます。
ヒント3:外部キーを削除して再作成する
インデックスと同様に、外部キーの制約もバルクロードのパフォーマンスに影響を与える可能性があります。これは、挿入された各行の各外部キーが、対応する主キーの存在を確認する必要があるためです。舞台裏では、PostgreSQLはトリガーを使用してチェックを実行します。多数の行をロードする場合、このトリガーは行ごとに起動する必要があり、オーバーヘッドが増加します。
ビジネスルールで制限されていない限り、ターゲットテーブルからすべての外部キーを削除し、単一のトランザクションでデータをロードし、トランザクションをコミットした後に外部キーを再作成することをお勧めします。
ALTER TABLE <target_table>
DROP CONSTRAINT <foreign_key_constraint>
BEGIN TRANSACTION
<bulk data insert operations…>
COMMIT
ALTER TABLE <target_table>
ADD CONSTRAINT <foreign key constraint>
FOREIGN KEY (<foreign_key_field>)
REFERENCES <parent_table>(<primary key field>)...
もう一度、 maintenance_work_memを増やします 構成パラメーターは、外部キー制約の再作成のパフォーマンスを向上させることができます。
ヒント4:トリガーを無効にする
INSERTまたはDELETEトリガー(ロードプロセスにターゲットテーブルからのレコードの削除も含まれる場合)は、バルクデータのロードに遅延を引き起こす可能性があります。これは、各トリガーには、チェックする必要のあるロジックと、各行がINSERTまたはDELETEされた直後に完了する必要のある操作があるためです。
データを一括ロードする前にターゲットテーブルのすべてのトリガーを無効にし、ロードの終了後に有効にすることをお勧めします。 ALLトリガーを無効にすると、外部キー制約チェックを実施するシステムトリガーも含まれます。
ALTER TABLE <target table> DISABLE TRIGGER ALL
<bulk data insert operations…>
ALTER TABLE <target table> ENABLE TRIGGER ALL
ヒント5:COPYコマンドを使用する
PostgreSQLのコピーの使用をお勧めします 1つまたは複数のファイルからデータをロードするコマンド。 COPYは、バルクデータロード用に最適化されています。多数のINSERTステートメントや複数値のINSERTを実行するよりも効率的です。
COPY <target table> [( column1>, … , <column_n>)]
FROM '<file_name_and_path>'
WITH (<option1>, <option2>, … , <option_n>)
COPYを使用するその他の利点は次のとおりです。
- テキストとバイナリファイルの両方のインポートをサポートします
- 本質的にトランザクションです
- 入力ファイルの構造を指定できます
- WHERE句を使用して条件付きでデータを読み込むことができます
ヒント6:複数値のINSERTを使用する
数千または数十万のINSERTステートメントを実行することは、大量のデータをロードするための適切な選択ではない可能性があります。これは、個々のINSERTコマンドをクエリオプティマイザによって解析および準備し、すべての制約チェックを実行し、個別のトランザクションとして実行して、WALにログインする必要があるためです。複数値の単一のINSERTステートメントを使用すると、このオーバーヘッドを節約できます。
INSERT INTO <target_table> (<column1>, <column2>, …, <column_n>)
VALUES
(<value a>, <value b>, …, <value x>),
(<value 1>, <value 2>, …, <value n>),
(<value A>, <value B>, …, <value Z>),
(<value i>, <value ii>, …, <value L>),
...
複数値のINSERTのパフォーマンスは、既存のインデックスの影響を受けます。コマンドを実行する前にインデックスを削除し、後でインデックスを再作成することをお勧めします。
注意すべきもう1つの領域は、複数値のINSERTを実行するためにPostgreSQLが使用できるメモリの量です。複数値のINSERTを実行する場合、多数の入力値をRAMに収める必要があり、十分なメモリが利用可能でない限り、プロセスが失敗する可能性があります。
effective_cache_sizeを設定することをお勧めします パラメータを50%に設定し、 shared_buffer マシンの合計RAMの25%にパラメータを設定します。また、安全のために、各ステートメントが1000行の値を持つ一連の複数値のINSERTを実行しています。
ヒント7:ANALYZEを実行する
これは、バルクデータインポートのパフォーマンスの向上とは関係ありませんが、 ANALYZEを実行することを強くお勧めします。 直後のターゲットテーブルに対するコマンド 一括インポート。新しい行が多数あると、列のデータ分布が大幅に歪められ、テーブルの既存の統計が古くなります。クエリオプティマイザが古い統計を使用する場合、クエリのパフォーマンスが許容できないほど低下する可能性があります。 ANALYZEコマンドを実行すると、既存の統計が確実に更新されます。
最終的な考え
データベースアプリケーションの場合、データの一括インポートは毎日行われるとは限りませんが、実行時にクエリのパフォーマンスに影響があります。そのため、読み込み時間を可能な限り最小限に抑える必要があります。驚きを最小限に抑えるためにDBAができることの1つは、同様のサーバー仕様とPostgreSQL構成を使用して開発環境またはステージング環境で負荷の最適化をテストすることです。データ読み込みのシナリオはそれぞれ異なります。それぞれの方法を試して、効果的な方法を見つけるのが最善です。