2

複数のプロセスがテーブルに挿入し、単一のプロセスがそこから選択するデータベース シナリオ (私は Oracle を使用しています) があります。テーブルは基本的に中間ストレージとして使用され、複数のプロセス (以下ではライターと呼ばれます) がログ イベントを書き込み、そこから単一のプロセス (以下ではリーダーと呼ばれます) がさらに処理するためにイベントを読み取ります。リーダーは、テーブルに挿入されたすべてのイベントを読み取る必要があります。

現在、これは、挿入された各レコードに昇順の ID が割り当てられることによって行われます。リーダーは、以前に読み取ったブロックの最大 ID よりも ID が大きいエントリのブロックをテーブルから定期的に選択します。例:

SELECT
  *
FROM
  TRANSACTION_LOG
WHERE
  id > (
    SELECT
      last_id
    FROM
      READER_STATUS
   );

このアプローチの問題は、ライターが同時に動作するため、行が昇順で割り当てられている場合でも、割り当てられた ID に従って行が常に順番に挿入されるとは限らないことです。つまり、id=100 の行は、id=110 のレコードの後に​​書き込まれることがあります。これは、id=110 の行を書き込むプロセスが、レコード id=100 を書き込むプロセスの後に開始されたが、最初にコミットされたためです。これにより、リーダーが id=110 の行を既に読み取った場合、id=100 の行が失われる可能性があります。

ライターにテーブルの排他ロックを強制すると、ライターが順次挿入し、リーダーが未処理のコミットを待機するようになるため、問題が解決します。ただし、これはおそらくそれほど高速ではありません。

私の考えでは、Reader は、未解決の Writer コミットを待ってから読み取るだけで十分です。つまり、すべてのライターが終了するまで、リーダーが読み取りを行っている限り、ライターは同時に動作し続ける可能性があります。

私の質問は次のとおり
です。ライター プロセスの未処理のコミットを待機するようにリーダー プロセスに指示するにはどうすればよいですか? 上記の問題に対する代替案も歓迎します。

4

5 に答える 5

1

リーダー プロセスでテーブルに排他ロックを設定することができます。これは、すべてのライターが終了して行ロックを解放するまで待機するため、未解決のライター トランザクションがないことを確認できます。

于 2008-09-30T09:45:57.900 に答える
1

同時実行性とスループットを妨げる可能性のあるロックは行いません。

行ごとに処理したログ行を追跡する場合は、Reader_Status テーブルも必要ありません。

これが私がすることです: ログテーブルに新しい列を追加します。たとえば、「処理済み」と呼びます。ブール値にします。デフォルトは false (または小さい整数、デフォルトは 0 など) です。ライターは、挿入時にデフォルト値を使用します。

リーダーが処理するレコードの次のブロックを照会するとき、彼は処理済みが false で id 値が低い行を照会します。

SELECT * FROM Transaction_Log
WHERE processed = 0
ORDER BY id
LIMIT 10;

それらを処理するとき、Reader は UPDATE を使用して処理済みを false から true に変更します。したがって、Reader が次にレコードのブロックに対してクエリを実行するとき、既に処理した行を取得することはないと確信しています。

UPDATE Transaction_Log
SET processed = 1
WHERE id = ?;  -- do this for each row processed

この UPDATE は、Writer によって行われる INSERT 操作と競合してはなりません。

行が他のライターによって順不同でコミットされた場合、リーダーが常に ID 列の最小値から最大値の順に処理する場合、リーダーは次にクエリを実行するときにそれらの行を確認できます。

于 2008-09-30T17:16:22.233 に答える
1

興味深い問題です。素敵なソリューションを構築しているようですね。
お役に立てれば幸いです。

いくつかの提案...

ライターのステータス

last_id フィールドを持つテーブル WRITER_STATUS を作成できます。各ライターは、ログに書き込む ID を書き込む前にこのテーブルを更新しますが、その ID が last_id の現在の値より大きい場合に限ります。

リーダーもこのテーブルをチェックし、まだ書き込みを行っていないライターがあるかどうかを認識します。

リーダーログ

これはより効率的かもしれません。
リーダーは読み取りを行った後、取得したレコードに穴がないかチェックします。
次に、欠落している ID を MISSING_IDS テーブルに記録し、次の読み取りのために次のようなことを行います。

SELECT *
FROM   TRANSACTION_LOG
WHERE  id > (SELECT last_id
             FROM   READER_STATUS)
OR     id IN ( SELECT id from MISSING_IDS ) 
于 2008-09-30T09:31:41.820 に答える
0

Reader によって処理されることがわかっているのでlast_id、次の作業項目を次の方法で要求できます。

select * from Transaction_log where id = (
  select last_id + 1 /* or whatever increment your sequencer has */
    from Reader_status)
于 2008-09-30T18:46:29.600 に答える
0

AJ の解決策 (リンク)に同意します。さらに、次の提案は、穴の数を減らすのに役立つ場合があります。

1) Oracle Sequenceid の作成に使用しauto-increment、次のように使用します。

INSERT INTO transaction_table VALUES(id__seq.nextval, <other columns>);

2)autoCommit(true)挿入がすぐにコミットされるように使用します。

この 2 つの手順により、穴の数が大幅に減少します。それでも、一部の挿入が最初に開始されたが、後でコミットされ、その間に読み取り操作が発生した可能性があります。

于 2008-10-01T03:46:52.263 に答える