2009-10-16 69 views
2

我有一个数据库表,我需要拉一行,测试用户输入的匹配,然后更新行来标识匹配的用户。如果出现竞争状况,我需要确保第一位用户的更新不会被其他用户覆盖。PHP,mysqli和表锁?

要做到这一点,我打算:
1.逐行读取
2.锁定表再次
3.阅读行,并比较原始行
4.如果相匹配的行更新,否则什么也不做(另一个用户已经更新了该行)

根据我在Google上找到的信息,我预计锁表语句会阻塞,直到获得锁。我在PHP中设置了一个小测试脚本,它会停顿10秒,以便我有时间手动创建竞争条件。

// attempt to increment the victor number 
$aData["round_id"] = $DATABASE["round_id"]; 

// routine to execute a SELECT on database (ommited for brevity) 
$aRound = $oRound->getInfo($aData); 

echo "Initial Round Data:"; 
print_r($aRound); 

echo "Locking..."; 
echo $oRound->lock(); 

echo "Stalling to allow for conflict..."; 
sleep(10); 
echo "Awake..."; 

$aLockedRound = $oRound->getInfo($aData); 
if($aRound["victor_nation"] == $aLockedRound["victor_nation"]){ 
    $aData["victor_nation"] = $aRound["victor_nation"] + 1; 
    $oRound->update($aData); 
    echo "Incremented Victor Nation"; 
} 

在锁定常规定义为

function lock(){ 
     global $oDatabase; 
     $iReturn = 0; 

     // lock the table 
     $iReturn = $oDatabase->m_oConnection->query("LOCK TABLES round WRITE");  
     return $iReturn; 
    } 

以上,$oDatabase->m_oConnection的是,我用它来在数据库上执行准备语句的mysqli的连接。

当我运行我的测试脚本时,我启动第一个用户并等待“停顿以允许冲突...”,然后启动第二个脚本。关于第二个脚本,我希望它阻止在“锁定...”,但是,第二个脚本也继续“停顿以允许冲突......”。由于LOCK陈述似乎没有被阻塞,也没有返回获取锁的任何指示器(返回值被回显并且空白),所以我不清楚我是否实际上获取了锁。即使我是,我不知道如何继续。

任何指针?

+0

Innodb或MyIsam表? – 2009-10-16 20:15:19

+0

该表是MyIsam – 2009-10-20 00:42:57

回答

3

故障排除:您可以通过尝试使用未锁定的另一个表来测试表锁成功。如果您获得了锁定,则尝试写入未包含在锁定语句中的表格应该会生成错误。

您可能需要考虑替代解决方案。作为where子句的一部分,不要锁定,而是执行包含已更改元素的更新。如果您读取数据后发生更改,则更新将“失败”并返回零行修改。这消除了桌面锁,以及随之而来的所有可怕的恐怖,包括死锁。 Innodb或MyIsam表吗?

+0

谢谢查尔斯!完全避免使用where claus工作得很好。 – 2009-10-20 00:45:06