1

やりたいことは、同じスクリプトを cron で数分ごとに実行することです。

スクリプトはデータベースから読み取ったデータを処理する必要があるため、毎回異なる行で作業する必要があることは明らかです。

私のコンセプトは、行ロックを使用して各インスタンスが異なる行で機能することを確認することでしたが、そのようには機能していないようです。このように行ロックを使用することさえ可能ですか? 他のソリューションはありますか?

例:

while($c < $limit) {
   $sql=mysql_query("SELECT * FROM table WHERE ... LIMIT 1 FOR UPDATE");
   $data=mysql_fetch_assoc($sql);

   (process data)

   mysql_query("update table set value=spmething, timestamp=NOW()");
   $c++;
}

基本的に必要なのは、SCRIPT1 がテーブルから R1 を読み取ることです。SCRIPT2 は R2 を読み取ります (条件に一致する次のロックされていない行)

編集: たとえば、1) テーブルに URL のリストが格納されているとします。2) スクリプトは URL が応答するかどうかを確認し、データベースのステータス (およびタイムスタンプ) を更新します。

4

2 に答える 2

4

これは本質的に2つの別々の問題として扱われるべきです:

  1. 各労働者が処理する仕事を見つける。理想的には、これは非常に効率的であり、次に来るステップ2の失敗を先制的に回避する必要があります。
  2. 各ジョブが最大で1回または正確に1回処理されるようにします。何が起こっても、同じジョブを複数のワーカーが同時に処理することはできません。バギー/クラッシュしたワーカーが原因でジョブが失われないようにすることをお勧めします。

どちらの問題にも、実行可能な解決策が複数あります。私の好みについていくつか提案します:

処理する仕事を見つける

低速システムの場合は、最新の未処理のジョブを探すだけで十分です。あなたはまだその仕事を引き受けたくはありません、ただそれを候補者として特定してください。これは次のようになります。

SELECT id FROM jobs ORDER BY created_at ASC LIMIT 1

(これにより、最も古いジョブ(FIFOオーダー)が最初に処理され、処理後に行が削除されると想定されることに注意してください。)

仕事を要求する

この単純な例では、これは次のように単純になります(物事を不明確にする可能性のある最適化を回避していることに注意してください)。

BEGIN;
SELECT * FROM jobs WHERE id = <id> FOR UPDATE;
DELETE FROM jobs WHERE id = <id>;
COMMIT;

SELECTによって照会されたときにリターンがジョブを返す場合、idこれでロックされました。別のワーカーがすでにこのジョブを引き受けている場合は、空のセットが返されるため、別のジョブを探す必要があります。SELECT ... FOR UPDATE2人の労働者が同じ仕事を求めて競争している場合、前のステートメントが普遍的に真実であるように、彼らはそれ以降、お互いをブロックします。これにより、各ジョブが最大で1回処理されるようになります。でも...

ジョブを1回だけ処理する

以前の設計のリスクは、ワーカーがジョブを取得し、それを処理できず、クラッシュすることです。仕事は今失われています。そのため、ほとんどのジョブ処理システムは、要求時にジョブを削除せず、代わりに、一部のワーカーによって要求されたものとしてマークを付け、ジョブ再利用システムを実装します。

jobこれは、テーブル内の追加の列または別のテーブルのいずれかを使用してクレーム自体を追跡することで実現できclaimます。通常、ホスト名、PIDなどのワーカーに関する情報が書き込まれ(claim_description)、クレームには有効期限(claim_expires_at)が1時間先などに提供されます。次に、追加のプロセスがそれらのクレームを処理し、有効期限を過ぎたクレームをトランザクションで解放します(claim_expires_at < NOW())。次に、ジョブを要求するにはclaim_expires_at IS NULL、選択時とで要求するときの両方で、ジョブ行の要求()をチェックする必要がありますSELECT ... FOR UPDATE

このソリューションにはまだ問題があることに注意してください。ジョブが正常に処理されたが、ジョブが完了として正常にマークされる前にワーカーがクラッシュした場合、最終的にクレームを解放してジョブを再処理する可能性があります。読者の練習問題として残されている、より高度なシステムを必要とする修正。;)

于 2013-02-21T01:34:53.860 に答える
1

行を一度だけ読み取る場合は、is_processed列を作成し、処理した行でその列を更新するだけです。次に、最初の行を単純にクエリできますis_processed = 0

于 2013-02-07T20:19:36.107 に答える