1

何百ものビジネス ユニットから毎晩レポートを受信するサーバーを開発しています。レポートは現在、暗号化された csv ファイルです。合計で、レポートは毎日 500,000 から 1,000,000 レコードに達し、後で使用するためにデータベースに保存されます。

送信ごとに一連の PreparedStatements を作成しました。これらのステートメントは、実行およびコミットする前に 50 レコードをバッチ処理するために使用されます。各レコードは、最大 20 のデータベース挿入を引き起こす可能性があります。送信がキューに入れられ、1 つずつ処理される場合は、すべて問題ありません。

これを同時に実行しようとしたときに、異なるスレッドが PreparedStatements のまったく同じインスタンスを取得していることに気付きました。これにより、次の問題が発生しました

  1. 複数のスレッドが同じバッチにステートメントを追加しました
  2. スレッドのいずれかがそうする時が来たと判断したときにバッチが実行されていました
  3. 一部のスレッドが一部のステートメントを使用する時間がなかったため、データベースがその制約を満たしていないときにコミットが呼び出されました

問題は、ステートメント キャッシュから既存のステートメントを再利用する代わりに、準備済みステートメントを強制的に作成する方法があるかどうかです。

そうでない場合、状況を処理するためのより良い方法はありません

  • ステートメント/接続プーリングを持たないバッチ用に別のデータ ソースを作成する
  • データベースからの制約の削除。挿入順序はもはや問題ではありません
  • 順次処理の強制

編集:問題を明確にする試み

スレッド 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 がコミットされているため、データベースは「いいえ」と言います。

これは、手動の同期と回答で指摘することで回避できますが、各スレッドにローカルなロジックを適用するのではなく、各バッチのサイズを個別に追跡する必要があります。

4

3 に答える 3

1

単一の接続インスタンスから複数のステートメントを使用しようとしていますか? IMO、あなたが説明する動作には接続プールが推奨されます。別の方法は、手動で同期することです。

于 2009-08-25T09:57:28.777 に答える
1

ソリューションはベンダー固有です。

コードがサーブレットの下で実行される場合、webapp でデータソースを構成することで問題を解決できる場合があります。私は Tomcat の下で Oracle ドライバーを使用してこれを行いましたが、他のアプリケーション サーバーにも接続プールを構成する同様の方法があると確信しています。

コードがスタンドアロンの場合は、ベンダー固有の API を使用する必要があります。Oracle を本番データベースとしてターゲットにするため、Oracle JDBC ドライバーの簡単な例を次に示します。

import oracle.jdbc.OracleConnection;

...

public static void disableStatementCaching(java.sql.Connection conn)
        throws SQLException {
    ((OracleConnection)conn).setImplicitCachingEnabled(false);
}

...

詳細については、Oracle 10.2 のJDBC 開発ガイドを参照してください。

于 2009-08-25T14:48:18.443 に答える
0

私の現在の解決策は、心配するのをやめて、共有バッチを愛し始めることです。処理アルゴリズムを2つのフェーズに分割しました

  1. N個のレコードのセットを解析し、それらを中間形式で保存します
  2. 現在のスレッドにロックが付与されたときに、N個のレコードのセットをバッチとして永続化します

これにより、解析を同時およびバッチ処理で順次実行できます。スレッド間の待機時間を最小限に抑えるためのスイートスポットを見つける必要があります。

スイートスポットの探求は、ある種の2段階ロックスキームの実装につながる可能性があります。つまり、各スレッドに好きなように実行させ、コミット時に、実際のバッチ実行の前にすべてのスレッドが現在のレコードを完了していることを確認します。

後者のソリューションでは、問題が発生するかどうかはテストしていませんが、PreparedStatementごとにパラメーター設定を同期する必要がある場合があります。そうすべき。

于 2009-08-28T05:39:46.233 に答える