web-dev-qa-db-ja.com

Postgresでテーブル全体を抽出する最速の方法

50Mレコードのテーブルをファイルにダンプしようとしています。私の目標は、このアクションが実行される時間を短縮することです。通常はCOPY metrics TO 'metrics.csv' DELIMITER ',' CSV;を使用します。最良の場合、これには1時間ほどかかることがあります。また、データをプレーンな形式でエクスポートすることにも興味があります(pd_dumpディレクトリの使用は避けてください)。

アイデアの1つは、テーブル全体を同じサイズのピースに分割する条件またはカーソルでこのテーブルに何らかの方法でアクセスすることです。これにより、たとえば2つのコピークエリを同時に実行して時間を半分に減らすことができます。

例:

COPY (SELECT * FROM metrics WHERE id < 25000000) TO 'metrics_1.csv' DELIMITER ',' CSV;
COPY (SELECT * FROM metrics WHERE id >= 25000000) TO 'metrics_2.csv' DELIMITER ',' CSV;

それらの条件で作成された部分インデックスは役に立ちますか?

テーブルのこの部分コピーダンプを実装するための良い方法はありますか?このテーブルをより速くダンプする他の解決策はありますか?

Postgresql 11/100GB RAM/20コア。

COPYとのいくつかの並列化の後、IO境界がボトルネックになっていないようです。

enter image description here

3
Imanol Y.

主キーで範囲クエリを使用するという現在の考えは、おそらく最善の策です。部分インデックスがどのように役立つかはわかりません。全体で合計インデックスになる一連の部分インデックスが必要であり、BTREEインデックスは無意味な範囲クエリに使用されます。ただし、このメソッドが成功するかどうかは、テーブルの行が主キー値によって物理的にほぼ順序付けられているかどうかに依存する可能性があります。 CLUSTERコマンドを使用してこの順序付けを強制できますが、それ自体は非常にコストのかかる操作です。

PostgreSQLにこれを並列化させる場合は、すべてを選択するダミークエリを使用します。

COPY (SELECT * FROM metrics) TO 'metrics.csv' DELIMITER ',' CSV;

また、「parallel_Tuple_cost設定」を劇的に下げて、おそらくゼロにする必要もあります。ただし、ボトルネック(ディスクIOでない場合)がデータを内部バイナリ形式からCOPYによって出力されるテキスト形式に変換することにボトルネックがあるため、これは実際の改善をもたらす可能性は低いです。この変換は常に、並列ワーカーではなくリーダープロセスで行われます。

これを回避するには、クエリを記述して、手間のかかる作業がクエリ自体で明示的に行われるようにします。

COPY (SELECT col1::text, col2::text, col3::text, ... FROM metrics) TO 'metrics.csv' DELIMITER ',' CSV;

リードプロセスは、エスケープ/引用符で囲む必要があるものがないか、すべてのテキストをスキャンする必要がありますが、少なくともJSONBをすべての行のテキストに変換する必要はありません。私の手では、7つの並列ワーカー(リーダーを含む8つのプロセス)を使用することで、COPY...TO 半分に。オーバーヘッドは、パラレルワーカーからリードプロセスへの通信になります。これを削除する方法は、インデックス付き範囲クエリの元の提案を別のセッションに戻すことです。

3
jjanes