PHP/MySQL 临界区

PHP/MySQL Critical section

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

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

$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;

}

如何使取消/完成操作成为临界区?第二个操作必须失败。

代码很好,有一个例外:将 FOR UPDATE 添加到初始 SELECT。这足以阻止第二次按钮按下,直到第一个 DELETE 发生,从而导致第二个 "failing".

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

Note Locking of rows for update using SELECT FOR UPDATE only applies when autocommit is disabled (either by beginning transaction with START TRANSACTION or by setting autocommit to 0. If autocommit is enabled, the rows matching the specification are not locked.

为了完整起见的另一个解决方案:

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