Php 有没有办法使这个数据库锁更有效?
我编写这段代码是为了确保两个用户不能同时编辑同一表的同一行。我唯一的问题是,它连接到数据库3次,一次用于添加新锁,一次用于检查它是否是该行的唯一锁,还有一次用于删除锁或检索数据以供用户编辑。我真的不喜欢这样,但这是我能想象的唯一方法 有没有办法让这更有效Php 有没有办法使这个数据库锁更有效?,php,mysql,Php,Mysql,我编写这段代码是为了确保两个用户不能同时编辑同一表的同一行。我唯一的问题是,它连接到数据库3次,一次用于添加新锁,一次用于检查它是否是该行的唯一锁,还有一次用于删除锁或检索数据以供用户编辑。我真的不喜欢这样,但这是我能想象的唯一方法 有没有办法让这更有效 <?php $CountryID = $_GET["CountryID"]; $userID = $_SESSION["userID"]; $currentTime = date('Y-m-d H:i:s'); try{ incl
<?php
$CountryID = $_GET["CountryID"];
$userID = $_SESSION["userID"];
$currentTime = date('Y-m-d H:i:s');
try{
include_once 'PDO.php';
//Adds a new lock into the database.
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$sth = $dbh->prepare('INSERT INTO lock (UserID, TableID, RowID) VALUES (?, ?, ?)');
$sth->execute(array($userID,1,$CountryID));
$insertedID = $dbh->lastInsertId();
//Checks to see if there is more than one lock for this table/row. If there is more than 1 row, it will check to make sure that all OTHER rows are older than 5 minutes old incase of broken locks.
$sth = $dbh->prepare('SELECT * from lock where TableID = ? AND RowID = ?');
$sth->execute(array(1,$CountryID));
$rowCount = $sth ->rowCount();
$locked = false;
if ($rowCount >1 ){
foreach($sth as $row){
if ($row['LockID'] != $insertedID AND (abs(strtotime($currentTime) - strtotime($row['Date']))) < 300){
$locked = true;
break;
}
}
}
if ($locked){
//Delete the lock we put in first, and tell the user that someone is already editing that field.
$sth = $dbh->prepare('DELETE from lock where LockID = ?');
$sth->execute(array($insertedID));
echo "Row is currently being edited.";
}else{
//Don't delete the lock, and get data from the country table.
echo "Row isn't being edited.";
$sth = $dbh->prepare('SELECT * from country where CountryID = ?');
$sth->execute(array($CountryID));
}
}catch (PDOException $e){
echo "Something went wrong: " . $e->getMessage();
}
$dbh = null;
?>
看起来您需要一个用于建议性持久锁定的方案。也就是说,您希望将锁保持在相对较长的时间内——比DBMS事务从web应用程序中合理预期的时间长。我称之为“咨询”,因为它不是DBMS强制执行的“强制性”
你很接近。我建议您使用复合主键(TableID,RowID)
定义锁表。这样,尝试将重复记录插入该表将失败。忘记锁ID
。你不需要它UserID
很有用,因为它会提示您诊断故障
然后,要设置一个锁(在表1的示例中,第$CountryID行),您将按如下方式进行插入:
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); /* only once*/
$sth = $dbh->prepare('INSERT INTO lock (UserID, TableID, RowID) VALUES (?, ?, ?)');
$locked = true;
try {
$sth->execute(array($userID,1,$CountryID));
}
catch (PDOException $e) {
$locked = false;
}
这很好,因为您可以通过将$locked设置为false来处理重复密钥错误(完整性约束冲突)。当$lock为false时,您知道其他人拥有锁,而您不能拥有它。这也很好,因为它的种族条件证明。如果两个用户竞相抢夺同一个锁,其中一个肯定会赢,另一个肯定会输
如果要释放锁,请以类似的方式执行删除操作
$sth = $dbh->prepare('DELETE FROM lock WHERE TableID = ? AND RowID = ?');
$it_was_locked = false;
$sth->execute(array(1,$CountryID));
if ($sth->rowCount() > 0) {
$it_was_locked = true;
}
在这里,变量$it_was_locked让您知道锁是否已经到位。在任何情况下,运行此命令后,锁将被清除
还有一件事。对于系统的完整性,请考虑定义一个锁超时。也许应该是10秒,也许应该是10小时:这取决于应用程序的用户体验需要。如果有人启动但没有完成事务,则超时将防止应用程序完全阻塞
然后,将locktime
列放入lock
表中,并自动将当前时间放入其中。您可以在表定义中使用这样的行来执行此操作
locktime TIMESTAMP DEFAULT CURRENT_TIMESTAMP
然后,无论何时插入锁,首先释放所有超时的锁,如下所示
$stclean = $dbh->prepare('DELETE FROM lock WHERE locktime < NOW() - 10 SECOND');
$sth = $dbh->prepare('INSERT INTO lock (UserID, TableID, RowID) VALUES (?, ?, ?)');
$locked = true;
$stclean->execute();
if ($stclean->rowCount() > 0) {
/* some timed-out locks were released; make an error log entry or whatever */
}
try {
$sth->execute(array($userID,1,$CountryID));
}
catch (PDOException $e) {
$locked = false;
}
$stclean=$dbh->prepare('DELETE FROM lock WHERE locktimeprepare('INSERT-INTO-lock(UserID,TableID,RowID)值(?,?);
$locked=true;
$stclean->execute();
如果($stclean->rowCount()>0){
/*释放了一些超时锁;请输入错误日志或其他内容*/
}
试一试{
$sth->execute(数组($userID,1,$CountryID));
}
渔获量{
$locked=false;
}
这种类型的东西可以提供一个可用的锁定方案。在进行系统测试时,您可能会经常查看此锁表,试图找出哪个模块忘记获取锁,哪个模块忘记释放锁。您使用的是什么访问方法?InnoDB?您是否看过使用SELECT。。。对于UPATE
?出于某些基于应用程序的原因,您是否排除了使用事务?如果不是,那么选择您要联锁的行的查询是什么样子的?我假设FOR UPDATE用于事务等行的短期锁定。在此应用程序中,用户有5分钟的时间编辑一行,直到他们的锁过期。非常感谢您的评论。目前锁在5分钟后就超时了,但我打算设置一个脚本,每天早上清理桌子或其他东西,但我认为你的方法更有效。