HTTP インターフェイスを介してメッセージを受信する Web アプリケーションがあります。
http://server/application?source=123&destination=234&text=hello
このリクエストには、送信者の ID、受信者の ID、およびメッセージのテキストが含まれています。
このメッセージは次のように処理する必要があります。
- ソースと宛先の両方に一致する User オブジェクトをデータベースから検索する
- オブジェクトのツリーの作成: メッセージ テキスト用のフィールドと、ソースと宛先用の 2 つの User オブジェクトを含む Message
- このツリーをデータベースに永続化します。
ツリーは、私が触れることができない他のアプリケーションによって読み込まれます。
Oracle をバッキング データベースとして使用し、JPA と Toplink をデータベース処理タスクに使用しています。可能であれば、私はこれらにとどまります。
多くの最適化を行わなくても、私の環境では最大 30 リクエスト/秒のスループットを達成できます。それほど多くはありません。1 秒あたり約 300 リクエストが必要です。そのため、パフォーマンスのボトルネックがどこにあるかを測定したところ、呼び出しにem.persist()
ほとんどの時間がかかっていることがわかりました。その行を単にコメントアウトすると、スループットは 1000 リクエスト/秒をはるかに超えます。
簡単な JDBC 呼び出しを使用して同じデータベースに 100 万のメッセージを永続化する小さなテスト アプリケーションを作成しようとしました。バッチ処理を使用しました。つまり、100 回の挿入とコミットを行い、すべてのレコードがデータベースに入るまで繰り返しました。このシナリオで 1 秒あたり約 500 リクエストのスループットを測定しましたが、これは私のニーズを満たすものでした。
ここで挿入パフォーマンスを最適化する必要があることは明らかです。ただし、前述したように、純粋な JDBC ではなく、JPA と Toplink を引き続き使用したいと考えています。
JPA と Toplink を使用してバッチ挿入を作成する方法を知っていますか? JPA 永続化のパフォーマンスを向上させるための他の手法をお勧めできますか?
追加情報:
「リクエスト/秒」とは、リクエストの合計数 / テストの開始からデータベースに書き込まれた最後のレコードまでの合計時間を意味します。
em.persist()
サーブレットとパーシスタの間にメモリ内キューを作成して、非同期呼び出しを試みました。パフォーマンスに大きく貢献しました。ただし、キューは非常に急速に増加し、アプリケーションは 1 秒あたり最大 200 のリクエストを継続的に受信するため、これは私にとって受け入れられる解決策ではありません。
この分離されたアプローチではem.persist()
、トランザクションをコミットする前に、100 ミリ秒のリクエストを収集し、収集したすべてのアイテムを呼び出しました。EntityManagerFactory は、各トランザクション間でキャッシュされます。