Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 有没有办法使这个数据库锁更有效?_Php_Mysql - Fatal编程技术网

Php 有没有办法使这个数据库锁更有效?

Php 有没有办法使这个数据库锁更有效?,php,mysql,Php,Mysql,我编写这段代码是为了确保两个用户不能同时编辑同一表的同一行。我唯一的问题是,它连接到数据库3次,一次用于添加新锁,一次用于检查它是否是该行的唯一锁,还有一次用于删除锁或检索数据以供用户编辑。我真的不喜欢这样,但这是我能想象的唯一方法 有没有办法让这更有效 <?php $CountryID = $_GET["CountryID"]; $userID = $_SESSION["userID"]; $currentTime = date('Y-m-d H:i:s'); try{ incl

我编写这段代码是为了确保两个用户不能同时编辑同一表的同一行。我唯一的问题是,它连接到数据库3次,一次用于添加新锁,一次用于检查它是否是该行的唯一锁,还有一次用于删除锁或检索数据以供用户编辑。我真的不喜欢这样,但这是我能想象的唯一方法

有没有办法让这更有效

<?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分钟后就超时了,但我打算设置一个脚本,每天早上清理桌子或其他东西,但我认为你的方法更有效。