2012-11-24 81 views
0

你好,我有一個瀏覽器遊戲,其中各種玩家安排可以持續一個或兩個小時的行動。 cronjob每分鐘檢查完成的所有動作(endtime < = unixtime())並完成它們(給予相關玩家獎勵等);PHP,MySQL和併發更新

最近發生了 - 比如說 - 100個動作要完成,而cronjob任務1在一分鐘內沒有完成,所以cronjob任務2被觸發,結果是所有完成兩次的動作。

我該如何避免這種情況再次發生?我必須爲會話使用特定的事務隔離代碼並保留行以進行更新?

PHP 5.3.18 是mysql 5.5.27 表引擎是INNODB

叫每分鐘當前的代碼是這樣的:

public function complete_expired_actions ($charflag = false) 
{  
    // Verifying if there are actions to complete... 

    $db = Database::instance(); 
    $db -> query("set autocommit = 0"); 
    $db -> query("begin"); 

    $sql = "select * from 
     character_actions 
     where status = 'running' 
     and endtime <= unix_timestamp()"; 

    $result = $db -> query ($sql) ;    

    // try-catch. Se si verifica un errore, l' azione che commette l' errore viene rollbackata 

    foreach ($result as $row) 
    { 
     try 
     { 

      $o = $this->factory($row -> action); 
      $o -> complete_action ($row); 


      if ($row -> cycle_flag == FALSE) 
      { 
       // non aperta ad attacchi SQl-injection perchè i parametri non sono passati via request 
      if ($charflag == true) 
        kohana::log('info', "-> Completing action: " . $row -> id . ' - ' . $row -> action . " for char: " . $row->character_id); 

       $db->query("update character_actions set status = 'completed' where id = " . $row->id); 
       // in ogni caso invalida la sessione! 
       Cache_Model::invalidate($row -> character_id);                  
      } 

      $db->query('commit'); 

      } catch (Kohana_Database_Exception $e) 
      { 
       kohana::log('error', kohana::debug($e->getMessage())); 
       kohana::log('error', 'An error occurred, rollbacking action:' . $row->action . '-' . $row->character_id); 
       $db->query("rollback");   
      } 

    }  

    $db->query("set autocommit = 1"); 

回答

0

在這種情況下,我用一個新列如:標誌表character_actions

然後在作業開始時我把這個查詢:

$uniqueid = time(); 
$sql="update character_actions set mark = '$uniqueid' where status = 'running' and endtime <= unix_timestamp() AND mark is NULL "; 

,那麼你的代碼可以

$sql = "select * from 
     character_actions 
     where status = 'running' 
     and mark = '$uniqueid'"; 

    $result = $db -> query ($sql) ; 

這種方法的限制,它的啓動不同的並行工程,減緩誘導更多的延遲和更加並行工程機...

可以引入來解決一個限制:

$lim= 100 ; // tune to finish the job in 60 seconds 
$sql="update character_actions set mark = '$uniqueid' where status = 'running' and endtime <= unix_timestamp() AND mark is NULL limit $lim "; 

當然會導致點歸屬延遲。

+0

好的,這將工作我猜,因爲下一個cronjobs將得到一個單獨的查詢集。謝謝,我會嘗試這個想法。 – Sunchaser