2017-01-04 8 views
1

私は、さまざまなタスクを実行する多くのコンソールアプリケーションを持っています。私は、PHPスクリプトからユニークなタスクを取得:MySQLテーブルからタスクを同時に取得する

$mysqli->autocommit(FALSE); 
$result = $mysqli->query("SELECT id, task FROM queue WHERE locked = 0 LIMIT 1 FOR UPDATE;"); 

while($row = $result->fetch_assoc()){ 
    $mysqli->query('UPDATE queue SET locked = 1 WHERE id="'.$row['id'].'";'); 
    $mysqli->commit(); 
    $response["response"]["task"] = $row["task"]; 
} 

$mysqli->close(); 
echo json_encode($response); 

時々私は、重複するタスクと、持っている「ロックを取得しようとしたときに見つかったデッドロックを、トランザクションを再起動してみてください」。 私は何が間違っていますか?

UPD: "ロック" 列に設定されたインデックスは、MySQLのドキュメントHow to Minimize and Handle Deadlocksから問題

+1

エラーとは関係ないと思いますが、なぜ 'LIMIT 1 'を指定するときに' while'ループを使用していますか?複数行を返すことはできないので、ループする必要はありません。 – Barmar

+1

'locked'カラムにインデックスがありますか?それを追加してみてください。 – Barmar

+0

アプリケーションは、このようなエラーをhttp://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.htmlに従って処理してから、エラーが発生した場合に再送信する必要があります。インデックスを設定し、トランザクションを小さく保つと、デッドロックのリスクを最小限に抑えることができます。 – user3606329

答えて

0

を解決:

あなたのテーブルに適切に選択されたインデックスを追加します。クエリで必要なインデックスレコード数を減らし、結果的にロック数を減らす必要があります。

インデックスをlocked列に追加するとこれが解決されます。それがなければ、SELECTクエリは、テーブルをスキャンして、locked = 0で行を探し、それを通過するすべての行をロックする必要があります。

WHERE句の列にインデックスを追加すると、そのレコードに直接移動してロックできます。他のレコードはロックされていないので、デッドロックの可能性は低くなります。

関連する問題