0

PHP、MySQL、および cron を使用して、非常に単純なジョブ キューイング システムを考え出しました。

  1. A()Cron は、 2 秒ごとに関数を呼び出す関数を持つ Web サイトを呼び出します。A()テーブルから行を検索して取得しますA
  2. 行を取得すると、列のA()値でその行を更新1しますworking
  3. A()次に、取得した行のデータに対して何かを行います
  4. A()B次に、処理ステップで取得した値を使用してテーブルに行を挿入します3

問題:テーブルから同じ行を複数回取得Bする関数が原因で、テーブルに重複する値がある場合があることに気付きました。A()A

上記の設計のどの部分が重複処理を許可しており、どのように修正する必要がありますか?

少なくとも実装方法を詳細に示さずに、rabbitMQ のようなものを提案しないでください。私は彼らのドキュメントのいくつかを読みましたが、それを実装する方法がわかりませんでした. ありがとう!

更新:ページ ( function を呼び出すc()) を毎分呼び出す cron ジョブがあります。c()functionを呼び出すループを 30 回実行するこの関数は、遅延A()に使用sleep()します。

4

2 に答える 2

2

提供された答えは良いです、ファイルロックはうまくいきます、しかし、あなたがMySQLを使っているので、私も答えると思いました。MySQLでは、 GET_LOCKおよびRELEASE_LOCKを使用して協調非同期ロックを実装できます。

*免責事項:以下の例はテストされていません。私は以前にこれに非常に近いものをうまく実装しました、そして以下は一般的な考えでした。

このGET_LOCK関数をMutexというPHPクラスでラップしたとしましょう。

class Mutex {
  private $_db = null;
  private $_resource = '';

  public function __construct($resource, Zend_Db_Adapter $db) {
    $this->resource = $resource;
    $this->_db      = $db;
  }

  // gets a lock for $this->_resource; you could add a $timeout value,
  // to pass as a 2nd parameter to GET_LOCK, but I'm leaving that
  // out for now
  public function getLock() {
    return (bool)$this->_db->fetchOne(
      'SELECT GET_LOCK(:resource)',
      array(
        ':resource' => $this->_resource
      ));
  }

  public function releaseLock($resource) {
    // using DO because I really don't care if this succeeds; 
    // when the PHP process terminates, the lock is released
    // so there is no worry about deadlock
    $this->_db->query(
      'DO RELEASE_LOCK(:resource)',
      array(
        ':resource' => $resource
      ));
  }
}

A()がテーブルからメソッドをフェッチする前に、ロックを要求します。リソース名として任意の文字列を使用できます。

class JobA {
  public function __construct(Zend_Db_Adapter $db) {
    $this->_db = $db;
  }

  public function A() {
    // I'm assuming A() is a class method and that the class somehow
    // acquired access to a MySQL database - pretend $this->db is a 
    // Zend_Db instance. The resource name can be an arbitrary 
    // string - I chose the class name in this case but it could be 
    // 'barglefarglenarg' or something.
    $mutex = new Mutex($this->db, get_class($this));

    // I choose to throw an exception but you could just as easily 
    // die silently and get out of the way for the next process, 
    // which often works better depending on the job
    if (!$mutex->getLock())
      throw new Exception('Unable to obtain lock.');

    // Got a lock, now select the rows you need without fear of 
    // any other process running A() getting the same rows as this
    // process - presumably you would update/flag the row so that the
    // next A() process will not select the same row when it finally
    // gets a lock. Once we have our data we release the lock

    $mutex->releaseLock();

    // Now we do whatever we need to do with the rows we selected
    // while we had the lock
  }
}

複数のプロセスが同じデータを選択および変更するシナリオを設計する場合、この種のことは非常に便利です。MySQLを使用する場合、移植性のために、ファイルロックメカニズムよりもこのデータベースアプローチを好みます。ロックメカニズムがファイルシステムの外部にある場合は、さまざまなプラットフォームでアプリをホストする方が簡単です。確かにそれは可能であり、それはうまく機能しますが、私の個人的な経験では、これは使いやすいと思いました。

アプリをデータベースエンジン間で移植可能にすることを計画している場合、このアプローチはおそらくうまくいきません。

于 2013-02-13T21:41:22.010 に答える
0

1 つの問題は、最初の処理である可能性があります。

Cron は、2 秒ごとにテーブル A から行を検索して取得する関数 A() を呼び出します。

スクリプトのこの部分の処理は、複数の行を選択する可能性があるため、インデックスのないテーブルでは 2 秒以上かかる場合があります。

これは、排他的なファイル ロックで解決できます。

ワークフローだけではないような気がします。基本的なコードが添付されていることを示すことができれば、コードにも問題がある可能性があります。

編集

前回の更新から判断すると、タイミングだと思います。

更新: ページ (関数 c() を呼び出す) を毎分呼び出す cron ジョブがあります。この関数 c() は、関数 A() を呼び出すループを 30 回実行し、sleep() を使用して遅延します。

それは多くのフープを飛び越えることであり、cron が重複しているスレッドの問題がある可能性があると思います。

于 2012-09-24T14:27:13.533 に答える