【问题标题】:How do I make sure (using PHP and MySQL) that when I commit a transaction, the entire transaction is successful?我如何确保(使用 PHP 和 MySQL)当我提交一个事务时,整个事务是成功的?
【发布时间】:2023-02-24 01:18:05
【问题描述】:

假设我是一个 PHP 应用程序,我使用 mysqli_begin_transaction 开始一个 innodb 事务,它返回 true。然后我做了一系列的插入。据我所知 --- 作为一个 PHP 应用程序 --- 所有插入都完美无缺。 (假设我检查了所有可能的错误代码。)据我了解,在我提交之前,整个事务可能会回滚 --- 由于死锁或其他原因。假设在我提交之前我延迟了一段时间做一些与 MySQL 无关的事情。进一步假设 --- 在此延迟期间 --- MySQL 进入必须回滚我的整个事务的情况。我声称我不知道它已回滚,因为此时我没有调用任何 MySQL 过程。 (PHP 中的 MySQL 驱动程序会以某种方式发现并抛出异常吗?我担心它不会,但我不知道。)当我准备好提交时,在我看来,MySQL 相对于我的连接,所以我将提交一个新的空事务。当我提交一个空事务时,MySQL 没有报告任何错误,所以我无法注意到我的整个事务都被回滚了。

问题.什么是一小段代码示例,我可以确定我的数据的责任完全掌握在 MySQL 手中而不是我的手中? (换句话说,我如何确保(使用 PHP 和 MySQL)当我提交事务时,整个事务成功地掌握在 MySQL 手中?)

参考. Previously,我曾经相信来自mysqli_commit 的返回布尔值。现在我的理解是我不能相信它。这个链接问题的答案可能是说我所需要的只是用 mysqli_report 尝试捕获而不是沉默。但我不相信。由于我无法轻易地重现隐式回滚,我正在寻找两件事 --- (1) 一个代码示例,我可以确定提交按预期发生,(2) 一种确定性的方式来重现隐式回滚,如果可能的话。在我看来,(2)并不容易,所以我先研究(1)。

版本.我正在运行 MySQL 5.7.38-log 和 PHP 7.4。

【问题讨论】:

  • 为什么您不再信任从 mysqli_commit() 返回的布尔值?这些注释是关于错误报告的,这是不同的。如果您不能假设它返回 true 时一切都很好,我会感到非常惊讶。

标签: php mysql mysqli transactions innodb


【解决方案1】:

顾名思义,数据库事务发生在数据库中。除非 MySQL 通知客户端 (PHP),否则 PHP 无法知道 MySQL 做了什么。 MySQL 只有在对服务器执行操作时才能通知客户端。所以在这方面,PHP 和 MySQL 之间的通信类似于客户端发出请求并期望响应的 HTTP 通信。

数据库事务确保数据作为单个原子操作提交(ACID 的一部分)。这意味着事务中的所有 DML 操作都被视为单个 DML 操作,即使由多个 DML 命令组成也是如此。要么他们都成功,要么他们都不成功。您可以相信数据库不会提交部分数据。

因此,正如我们在前两段中建立的那样,MySQL 将始终告诉您操作是成功还是失败,并且它永远不会提交事务的一部分,问题出现了:PHP 何时以及如何告诉 MySQL 提交事务?答案很简单:每当您希望成为事务一部分的所有 DML 操作都成功执行时,发送 COMMIT SQL 命令.

使用 mysqli(在 PDO 中几乎相同),它看起来像这样:

$mysqli->begin_transaction();
$stmt = $mysqli->prepare("INSERT INTO foo(text) VALUES(?)");
$stmt->execute(['bar']);
// more DML operations
$mysqli->commit();

就是这样!如果任何准备好的语句失败,PHP 将根据从 MySQL 收到的错误响应抛出异常。只要确保您启用了 mysqli 错误报告!如果 commit() 失败,则整个交易失败(ACID 中的 C)。

如果不启用错误报告,您可能会在没有注意到的情况下让其中一个查询失败。如果您要继续提交,您将最终提交更改而没有失败的更改。

我们有 ROLLBACK 命令的原因是 MySQL 不会隐式回滚作为事务的一部分并已成功执行的查询。只要连接会话保持打开状态,MySQL 就会等待 COMMITROLLBACK 结束事务。如果您想放弃更改并继续进行一些其他操作,甚至使用相同的连接再次重试整个事务,则需要回滚事务。 PHP 中的 try-catch 有助于实现这一点。有关更多信息,请参阅我的其他答案:How to start and end transaction in mysqli?


提醒一句:数据库事务是 ACID,但某些 DDL 命令会触发隐式提交。如果您没有意识到这一点并且将 DDL 命令作为事务的一部分执行,则可能会过早地结束事务并导致数据不一致。

【讨论】:

    猜你喜欢
    • 2011-02-22
    • 1970-01-01
    • 2012-05-22
    • 2016-03-22
    • 1970-01-01
    • 1970-01-01
    • 2011-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多