PHP / MySQL关键部分

时间:2015-05-13 23:34:55

标签: php mysql transactions innodb critical-section

我正在使用PHP与PDO和InnoDB表。

我只希望代码允许一个用户提交的操作完成,用户可以取消或完成。但是在用户发布这两个操作的情况下,我希望其中一个请求失败并回滚(现在没有发生),两者都在完成无例外/错误。我认为在检查它之后删除该行就足够了。

$pdo = new PDO();

try {

    $pdo->beginTransaction();

    $rowCheck = $pdo->query("SELECT * FROM table WHERE id=99")->rowCount();

    if ($rowCheck == 0)
        throw new RuntimeException("Row isn't there");

    $pdo->exec("DELETE FROM table WHERE id = 99");

    // either cancel, which does one bunch of queries. if (isset($_POST['cancel'])) ...
    // or complete, which does another bunch of queries. if (isset($_POST['complete'])) ...
    // do a bunch of queries on other tables here...

    $pdo->commit();

} catch (Exception $e) {

    $pdo->rollback();

    throw $e;

}

如何将取消/完成操作作为关键部分?第二次操作必须失败。

2 个答案:

答案 0 :(得分:1)

代码很好,但有一个例外:将FOR UPDATE添加到初始SELECT。这应该足以阻止第二个按钮按下直到第一个DELETE发生,从而导致第二个按钮“失败”。

https://dev.mysql.com/doc/refman/5.5/en/innodb-locking-reads.html

  

注意仅使用SELECT FOR UPDATE锁定要更新的行   禁用自动提交时(通过开始事务处理)   START TRANSACTION或将autocommit设置为0.如果是自动提交   启用后,与规范匹配的行不会被锁定。

答案 1 :(得分:1)

另一种解决方案只是为了完整性:

private function getLock() {
    $lock = $this->pdo->query("SELECT GET_LOCK('my_lock_name', 5)")->fetchColumn();

    if ($lock != "1")
        throw new RuntimeException("Lock was not gained: " . $lock);
}

private function releaseLock() {
    $releaseLock = $this->pdo->query("SELECT RELEASE_LOCK('my_lock_name')")->fetchColumn();

    if ($releaseLock != "1")
        throw new RuntimeException("Lock not properly released " . $releaseLock);
}

MySQL GET_LOCK() documentation