防止对同一脚本的两次调用选择相同的mysql行

时间:2011-10-08 16:31:51

标签: php mysql

以下脚本每5秒调用一次。问题是如果服务器响应缓慢,“blog”中的一个条目可以连续两次被选中,因为服务器还没有时间将“done”设置为“1”。是否有行业标准(或任何你称之为)的方法来防止这种情况发生?

$result = mysql_query("SELECT * FROM blogs WHERE done=0 LIMIT 1");
$rows = mysql_num_rows($result); //If there are no entries in with done set to 0, that means we've done them all; reset all entries to 0.
if($rows == 0)
{
    mysql_query("UPDATE blogs SET done=0 WHERE done=1");
}
else
{
    while($row = mysql_fetch_array($result))
    {
        mysql_query("UPDATE blogs SET done=1 WHERE id=$row[id]");
        // Do stuff
    }
}

认为我可以将其改为

while($row = mysql_fetch_array($result))
{
      if($row['done'] == 1){ die; }
      mysql_query("UPDATE blogs SET done=1 WHERE id=$row[id]");
      //Do stuff
}

但这会真正解决这个问题吗?我想如果有一种更好的方法可以在没有疑点的情况下真正防止它发生。

3 个答案:

答案 0 :(得分:2)

我会去交易。以下是另一个StackOverflow question

中的示例

只是一个问题:如果服务器更慢,会发生什么?例如,select statament需要很长时间(例如5秒),一旦完成(返回0行),就会执行新的select(返回1行或更多行)

MySQL documentation

答案 1 :(得分:2)

我认为阻止选择同一行的最佳方法是使用SELECT GET_LOCK("lock_name");SELECT RELEASE_LOCK("lock_name");。当你从mysql服务器获得锁定时,尝试获取锁定的其他处理将等待锁定被释放。以下是一个示例实现:

<?php
function getLock($lockName, $dbc) {
    $query = "SELECT GET_LOCK('".$lockName."', 0)";
    $result = mysql_query($query, $dbc);
    $lockResult = mysql_fetch_row($result);
    $lockResult = $lockResult[0];
    return $lockResult == 1 ? true : false;
}

function releaseLock($lockName, $dbc) {
    $query = "SELECT RELEASE_LOCK('".$lockName."')";
    $result = mysql_query($query, $dbc);
}

// CONNECT TO DATABASE
$dbc = mysql_connect('localhost', 'root', '');
mysql_select_db('test', $dbc);

$loopQueue = true;
$rowsProcessed = 0;

// MAIN QUEUE LOOP
while ($loopQueue) {

    // TRY UNTIL GETTING A LOCK
    $queueLockName = 'queue_lock_1';
    while (getLock($queueLockName, $dbc) === true) {

        // WE GOT THE LOCK, GET A QUEUE ROW WITH PENDING STATUS
        $query = 'SELECT * FROM test WHERE status = 0 ORDER BY ID ASC LIMIT 1';
        $result = mysql_query($query, $dbc);

        if (mysql_num_rows($result) < 1) {
            // SINCE WE DON"T HAVE ANY QUEUE ROWS, RELEASE THE LOCK
            releaseLock($queueLockName, $dbc);
            // WE DONT NEED TO LOOP THE MAIN QUEUE ANYMORE SINCE WE DONT HAVE ANY QUEUE ROWS PENDING
            $loopQueue = false;
            // BREAK THIS LOOP
            break;
        }

        // WE GOT THE QUEUE ROW, CONVERT IT TO ARRAY
        $queueRowArray = mysql_fetch_assoc($result);

        // UPDATE QUEUE ROW STATUS TO SENDING
        $query = 'UPDATE test SET status = 1 WHERE id = '.$queueRowArray['id'];
        mysql_query($query);

        // RELEASE THE LOCK SO OTHER JOBS CAN GET QUEUE ROWS
        releaseLock($queueLockName, $dbc);

        // DO STUFF ...

        // UPDATE QUEUE ROW STATUS TO PROCESSED
        $query = 'UPDATE test SET status = 2 WHERE id = '.$queueRowArray['id'];
        mysql_query($query);

        $rowsProcessed++;
    }
}

echo "\n\n".'process finished ('.$rowsProcessed.')'."\n\n";

答案 2 :(得分:1)

$result = mysql_query("SELECT * FROM blogs WHERE done=0 LIMIT 1");
$rows = mysql_num_rows($result); //If there are no entries in with done set to 0, that means we've done them all; reset all entries to 0.
if($rows == 0)
{
    mysql_query("UPDATE blogs SET done=0 WHERE done=1");
}
else
{
    while($row = mysql_fetch_array($result))
    {
        mysql_query("UPDATE blogs SET done=1 WHERE id=$row[id] AND done=0");
        if(mysql_affected_rows() != 1)
            die();
        // Do stuff
    }
}