web-dev-qa-db-ja.com

PostgreSQL:byteaフィールドへのバイナリデータのストリーミングが非常に遅い

バイナリデータをbyteaフィールドにストリーミングしようとしています。プロセスは非常に簡単です:

_loop until the end of the incoming stream
  UPDATE myTable SET data = data || $chunk WHERE id = myId
_
  • _$chunk_は、現在のストリームチャンクのバインディングです。
  • 行(myId)はすでにNULLデータで存在します
  • 各チャンクは約64kです
  • 最終的なデータは約4MBです

チャンクが格納されるにつれて、データの連結がますます遅くなることを想定して、すべてが適切に機能しています。

SQLの移植性を維持する必要があるため、「PostgreSQLラージオブジェクト」を使用できません。

このプロセスを最適化することは可能ですか?

  • 空のデータで事前に割り当ててから、overlay()を使用して各チャンクを配置しますか?
1

Postgresのように マルチバージョンモデル の行を更新するということは、新しいコンテンツでその行の2番目のコピーを作成することを意味します。物理的には、インプレース更新はありません。UPDATEは、新しいコンテンツのDELETE + INSERTに似ています。

したがって、上記のループでは、最初のチャンクは1回ではなくN回書き込まれ、2番目のチャンクはN-1回書き込まれ、3回目はN-2回繰り返されます。

さらに悪いことに、これらの書き込みはすべて、ジャーナリングのためにWALファイルに送信する必要があり、行のすべての中間バージョンを自動バキュームプロセスでピックアップして、最終的に破棄する必要があります。

これらのチャンクをクライアントでアセンブルできず、ストリーミングする必要があると仮定すると、次のようなことを行うと役立つ場合があります。

CREATE TEMPORARY sequence s;
CREATE TEMPORARY TABLE buffer(seq int default nextval('s'), chunk bytea);

-- buffer in temporary storage
LOOP 
   INSERT INTO buffer(chunk) VALUES ($chunk)
END LOOP

-- assemble in final storage
INSERT INTO permanent_table(data)
   SELECT string_agg(chunk,''::bytea order by seq) FROM buffer;

TRUNCATE (or DROP) TABLE buffer;

一時テーブルはWALログに記録されないため、少なくともチャンクは2回だけ(バッファーストレージに1回、最終の永続ストレージに1回)、ジャーナルに1回だけ書き込まれます。

1
Daniel Vérité