ちょっと近いものを開発した経験がありますが、まず、前の質問を解決するほうが先決かと思われます。
前回の質問: C++にてSQL発行部分をファイル出力に変更する際の最適化
で、
InsertやUpdate時にWhere文等で条件を追加しているため、 COPYコマンドは使用できません
とおっしゃってますが、実装されようとしているアプリの性能要件から、 COPY でのインポートが必須であると思われます。
それで、生データのテーブルと完成テーブルを分けてはどうでしょうか。分ける前提として、
- PostgreSQL はデータを一括して処理するほうが1件ずつ処理するより数倍早い
- 一括処理する塊は大きければ大きいほど有利である
というのがあります。つまり、COPY とか INSERT SELECT は普通の INSERT 文を実行するより数倍早いはずです。正確な数値で示すことができませんが、大量データ取込のいろいろ PostgreSQL編によると、数十倍の差があります。
複数回 INSERT VS INSERT SELECT のほうは、ちょうどいいのがぱっと見つけられなかったのですが、カーソルのループでINSERT100万とSELECT INSERT100万の速度比較してみたに ORACLE で10倍以上差がついている記事がありました。
具体的には、テーブルへの登録処理を2段のパイプラインに分けます。1段目の処理でデータを加工処理せずに受信したままの形でデータベースに登録します。この登録処理では速度を最大にするために TSV ファイル形式からのCOPY命令を利用します。今回の質問の 1. の考え方に近いです。
- データを受信したプログラムで TSVファイルを生成
- 生成したファイルから COPY で生データテーブルにインポートする
このとき、後の処理のために生データテーブルにはシリアルデータ型で通番をふっておきます。また、インポートを高速化するため、生データテーブルにはインデックスをはらないほうが良いでしょう。
2段目の処理で、生テーブルに登録されているデータを完成形テーブルに転記します。今回の質問の2.の考え方に近いです。
- 一定時間ごとのバッチ処理で生データテーブルの内容から完成形テーブルに INSERT 文で転記する(WHERE, JOIN,GROUP BY などの加工処理はこの転記時に実行する:参考 表をSELECTして別の表へINSERTする)
- 転記時に生テーブルどこまで転記したかを別テーブルに記録しておく(前述の通番を参照)
- 転記が終わった範囲のデータを生データテーブルから削除する(性能を上げるためにここをテーブルパーティショニングを使って DELETE ではなく DROP で掃除するようにする必要があります)
で、上記の設計が可能であれば、今回の質問については、 TSV ファイルで生データを出力し、C++ のAPIで COPY 文を実行するというのが最適解になるかと思います。
この方法ですと、1段目の処理と2段めの処理は同期する必要がありませんので、以下のメリットが得られると思います。
- 生データテーブルの読み込みのほうはかなりの性能が出ますので、取りこぼしの心配の方がなくなる
- 完成形テーブルへの転記の INSERT 文の性能がボトルネックになってくると思いますが、受信スループットにピークがあるような場合、ピーク時に遅れが出ても後で暇になったときに追いつかせることができます(単純に一定時間ごとに一定量処理するのではなく、遅れが出ている場合は処理量が増やすようにロジックを組む必要があります)
あと、ステマみたいで恐縮ですが、AWS Redshiftはこういう要件に強いです。