何百ものビジネス ユニットから毎晩レポートを受信するサーバーを開発しています。レポートは現在、暗号化された csv ファイルです。合計で、レポートは毎日 500,000 から 1,000,000 レコードに達し、後で使用するためにデータベースに保存されます。
送信ごとに一連の PreparedStatements を作成しました。これらのステートメントは、実行およびコミットする前に 50 レコードをバッチ処理するために使用されます。各レコードは、最大 20 のデータベース挿入を引き起こす可能性があります。送信がキューに入れられ、1 つずつ処理される場合は、すべて問題ありません。
これを同時に実行しようとしたときに、異なるスレッドが PreparedStatements のまったく同じインスタンスを取得していることに気付きました。これにより、次の問題が発生しました
- 複数のスレッドが同じバッチにステートメントを追加しました
- スレッドのいずれかがそうする時が来たと判断したときにバッチが実行されていました
- 一部のスレッドが一部のステートメントを使用する時間がなかったため、データベースがその制約を満たしていないときにコミットが呼び出されました
問題は、ステートメント キャッシュから既存のステートメントを再利用する代わりに、準備済みステートメントを強制的に作成する方法があるかどうかです。
そうでない場合、状況を処理するためのより良い方法はありません
- ステートメント/接続プーリングを持たないバッチ用に別のデータ ソースを作成する
- データベースからの制約の削除。挿入順序はもはや問題ではありません
- 順次処理の強制
編集:問題を明確にする試み
スレッド T1 と T2 があるとします。準備されたステートメント S1 と S2 があるとします。バッチ B1 と B2 があるとします。
S1 が使用されるたびに、B1 に追加されます。S2 が使用されるたびに、B2 に追加されます。コミットするときは、外部キー制約ごとに S2 の前に S1 をコミットする必要があります。
問題が発生する場合
- T1は送信を喜んで処理します
- T2は送信を無邪気に処理します
- T1 はステートメント S1 を使用して、s1a を含むバッチ B1 に s1a を追加します。
- T1 はステートメント S2 を使用して、s2a を含むバッチ B2 に s2a を追加します。
- T1 は、コミットする時が来たと判断します
- T1 は、s1a を含むバッチ B1 をコミットします
- T2 は S1 を使用して、s1b を含むバッチ B1 に s1b を追加します。
- T2 は S2 を使用して、s2a、s2b を含むバッチ B2 に s2b を追加します。
- T1 は、s2a、s2b を含むバッチ B1 をコミットします
- 外部キーで禁止されている s1b の前に s2b がコミットされているため、データベースは「いいえ」と言います。
これは、手動の同期と回答で指摘することで回避できますが、各スレッドにローカルなロジックを適用するのではなく、各バッチのサイズを個別に追跡する必要があります。