2012-01-12 17 views
1

Iは、簡略化のように表すことができるプライオリティキュー・テーブルを有する:priorityプライオリティ(最低最初)であり、status(0が取得するためにOKを意味する)アイテムの状態であるSQL優先キュー操作 - 落とし穴?

CREATE TABLE test (
    id int PRIMARY KEY, 
    priority int, 
    status int 
) 

を。タスクは、最も高い優先順位を持つ要素を取得し、そのステータスをリセットすることで、そのコードは、この(擬似コード)のようになります:

try = 0; 
while(try++ < max_tries) { 
    result = query("SELECT id FROM test WHERE status=0 ORDER BY priority LIMIT 0,1"); 
    if(result not empty) { 
     /* found a row, try to update */ 
     result2 = query("UPDATE test SET status=1 WHERE id={result[id]} AND status=0"); 
     if(affected_rows(result2) > 0) { 
     /* if update worked fine, we can use this ID */ 
     return result[id]; 
     } 
     /* otherwise try again */ 
} 
return NULL; 

このコードが実行する必要があります(適宜変更して、異なるLIMIT構文のように)任意のSQLデータベースに(現在の要件はMySQL、Oracle、SQL ServerおよびDB2ですが、それ以上の場合もあります)。私が知る限り、必要なすべてのDBは更新のために "影響を受ける行" APIをサポートしています。このアプローチの潜在的な問題または落とし穴?上記のステートメントでSELECT ... FOR UPDATEを使用する必要がありますか?はいの場合、なぜですか?

+0

トランザクションを使用する予定ですか? –

答えて

1

リストされたSELECT/UPDATEの組み合わせは、UPDATEのWHERE句による並行性の問題に対処しているようです。私はしかし、それが受け入れられた場合、ストアドプロシージャに傾いていた:SQLの風味に応じて、格納されたprocは、原子 "UPDATE ... RETURNING id INTO ..."で並行性の問題を取り除く可能性があります。その後、

  • 0 "のプロセスに"
  • 1 "を取得しても大丈夫"

を "完了" 状態:状態について

、彼らのようなものであればロックとして機能します。状態が0から1に変化した後、ジョブが実際に処理される前にプロセスが終了すると懸念があります。クリーンアップジョブが実行されるまで、レコードはロックされたままになります。もう1つの方法は、ロックに別のタイムスタンプ列を使用することです。次に、クエリに「WHERE now() - タイムアウト> NVL(lockTimestamp、開始時刻)」というフィルタが含まれ、status = 1の設定に頼るのではなくlockTimestampをnow()に設定することによってロックが行われます。このようにして、合理的な待機(タイムアウト)の後にロックが自動的に期限切れになり、そのアイテムが再び利用可能になり、次のプロセッサによって取り上げられます。クリーンアップ作業は必要ありません。

優先度の低いジョブが選択されない可能性のある飢餓の可能性はありますか?

注文を追加する必要がありますか?項目がすべて同じ優先順位の場合、最初に処理される項目(fifo、lifo)は重要ですか?

このフェッチルーチンが複数実行されている場合、複数のキュー項目が同時に処理され、処理順序が必ずしも順次ではないことに注意してください(process1はitem1を取得し、process2はitem2を取得し、process2は完了しますitem2、process1はitem1、...を完成します)、うまくいけばそれは大丈夫です。さもなければ、ロックは処理中のものがないことを確認する必要があります。

特に、SELECT ... FOR UPDATEの必要はありません。

+0

RETURNING id INTOはOracle専用ではないと思いますか?最小の変更でほとんどのSQL DBで動作するものが必要です。 – StasM

+0

@StasMあなたはそうです、あまりにも遍在しているようには見えません。 OracleとPostgreSQLはそれを持っていますが、それ以上のことは分かりません。私はストアドprocの使用法もオプションではないかもしれないと思いますが、実際にはこのような場合に格納されたprocのアイディアが、実装からapiを分割する能力のために好きです。アプリケーションは単に「getNext」と言って、ストアドプロシージャはそれ自身の言語機能を使って何をすべきかを判断します。 – Glenn

+0

DB2のそれ以降のバージョンは 'NEW FROM NEW TABLE(UPDATE ...) 'となりますが、これはiSeriesのバージョンにはありません(zSeries、LUWのみ、私は思う)。また、どのくらいの期間、ジョブがシステム内に留まることが予想されるかによって、クリーンアップジョブはどのインスタンスをロック解除するかを決定できません。 –