【问题标题】:How does mysqli::commit & mysqli::rollback work?mysqli::commit 和 mysqli::rollback 是如何工作的?
【发布时间】:2013-07-13 01:30:30
【问题描述】:

我已经阅读了在线 php 手册,但我仍然不确定这两个函数的工作方式:mysqli::commit 和 mysqli::rollback。

我要做的第一件事是:

$mysqli->autocommit(FALSE);

然后我做了一些查询:

$mysqli->query("...");
$mysqli->query("...");
$mysqli->query("...");

然后我提交由这 3 个查询组成的事务:

$mysqli->commit();

但是在这些查询之一不起作用的不幸情况下,是所有 3 个查询都被取消还是我必须自己调用回滚?我希望所有 3 个查询都是原子的,并且只被视为一个查询。如果一个查询失败,那么所有 3 个都应该失败并且没有任何效果。

我问这个是因为在我在手册页上看到的 cmets 中:http://php.net/manual/en/mysqli.commit.php 如果其中一个查询失败,用户 Lorenzo 会调用回滚。

如果 3 个查询是原子的,那么回滚有什么好处?我不明白。

编辑:这是我怀疑的代码示例:

<?php 
$all_query_ok=true; // our control variable 
$mysqli->autocommit(false);
//we make 4 inserts, the last one generates an error 
//if at least one query returns an error we change our control variable 
$mysqli->query("INSERT INTO myCity (id) VALUES (100)") ? null : $all_query_ok=false; 
$mysqli->query("INSERT INTO myCity (id) VALUES (200)") ? null : $all_query_ok=false; 
$mysqli->query("INSERT INTO myCity (id) VALUES (300)") ? null : $all_query_ok=false; 
$mysqli->query("INSERT INTO myCity (id) VALUES (100)") ? null : $all_query_ok=false; //duplicated PRIMARY KEY VALUE 

//now let's test our control variable 
$all_query_ok ? $mysqli->commit() : $mysqli->rollback(); 

$mysqli->close(); 
?>

我认为这段代码是错误的,因为如果任何查询失败并且$all_query_ok==false,那么您不需要进行回滚,因为事务没有被处理。我说的对吗?

【问题讨论】:

  • 开始工作块在哪里?你知道什么是自动提交吗?
  • 如果您将 sql 语句包装在 BEGIN TRANSACTION END 块中,则此方法有效。因此,如果您的任何语句在事务块内失败,它会回滚事务。
  • @N.B.我想我知道什么是自动提交:如果它打开了,那么我调用的每个查询都将立即由数据库处理,如果它被关闭,那么只有在 $mysqli->commit(); 时查询才会发送到数据库;被调用。
  • @Ariel - 自动提交是 InnoDB 的工作模式。 commit 除其他外,要求操作系统验证永久存储设备(在本例中为硬盘驱动器)是否已将信息写下并且它不在任何内存缓冲区中。这是一个昂贵的信息/动作,它使用了 1 个输入输出操作。当自动提交打开时,发送到数据库的每个查询将使用 1 个 I/O 抛出异常/如果失败则引发错误。关闭自动提交对事务来说基本上没有意义。您需要一个 begin work 块,而不是关闭自动提交。
  • @N.B.: BEGIN WORK(以及它推荐的标准化替代方案START TRANSACTION)只是为一个事务隐式关闭自动提交。即使在事务结束后,显式关闭自动提交也会使其关闭。就这个问题而言,差别不大。

标签: php mysql mysqli commit rollback


【解决方案1】:

我认为这段代码是错误的,因为如果任何查询失败并且 $all_query_ok==false 那么你不需要回滚,因为 交易未处理。我说的对吗?

不,如果单个 SQL 语句失败,则事务不会跟踪。

如果单个 SQL 语句失败,则 语句 会回滚(就像 @eggyal 的回答中所描述的那样) - 但 事务 仍处于打开状态。如果你现在调用commit,成功的语句不会回滚,你只是将“损坏的”数据插入到数据库中。你可以很容易地重现这个:

m> CREATE TABLE transtest (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
 name VARCHAR(100) NOT NULL DEFAULT '',
 CONSTRAINT UNIQUE KEY `uq_transtest_name` (name)) ENGINE=InnoDB;
Query OK, 0 rows affected (0.07 sec)

m> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

m> INSERT INTO transtest (name) VALUE ('foo');
Query OK, 1 row affected (0.00 sec)

m> INSERT INTO transtest (name) VALUE ('foo');
ERROR 1062 (23000): Duplicate entry 'foo' for key 'uq_transtest_name'

m> INSERT INTO transtest (name) VALUE ('bar');
Query OK, 1 row affected (0.00 sec)

m> COMMIT;
Query OK, 0 rows affected (0.02 sec)

m> SELECT * FROM transtest;
+----+------+
| id | name |
+----+------+
|  3 | bar  |
|  1 | foo  |
+----+------+
2 rows in set (0.00 sec)

尽管第二个 SQL 语句失败,但您看到 'foo' 和 'bar' 的插入成功 - 您甚至可以看到错误查询增加了 AUTO_INCREMENT-值。

所以你必须检查每个query-call 的结果,如果一个失败,调用rollback 撤消其他成功的查询。所以 Lorenzo 在 PHP 手册中的代码是有意义的。

强制 MySQL 回滚事务的唯一错误是“事务死锁”(这是 InnoDB 特有的,其他存储引擎可能会以不同的方式处理这些错误)。

【讨论】:

  • 完美!谢谢!正是我想知道的。所以我想我必须验证每一条语句并在任何查询出错时进行回滚。
  • 它有效,谢谢兄弟。 @vstm 感谢您提出这个问题,它节省了我的时间。 #爱丽儿
【解决方案2】:

InnoDB Error Handling 中所述:

InnoDB 中的错误处理并不总是与 SQL 标准中指定的相同。根据标准,SQL 语句期间的任何错误都应导致该语句回滚。 InnoDB 有时只回滚部分语句或整个事务。以下项目描述了InnoDB 如何执行错误处理:

  • 如果tablespace 中的文件空间不足,MySQL 会出现Table is full 错误,InnoDB 会回滚 SQL 语句。

  • 事务deadlock 导致InnoDBroll back 整个transaction。发生这种情况时重试整个事务。

    锁等待超时导致InnoDB 仅回滚等待锁并遇到超时的单个语句。 (要让整个事务回滚,请使用 --innodb_rollback_on_timeout 选项启动服务器。)如果使用当前行为,则重试该语句,如果使用 --innodb_rollback_on_timeout,则重试整个事务。

    死锁和锁等待超时在繁忙的服务器上都是正常的,应用程序必须意识到它们可能会发生并通过重试来处理它们。您可以通过在事务期间对数据的第一次更改和提交之间做尽可能少的工作来降低它们的可能性,从而使锁定保持尽可能短的时间和尽可能少的行数。有时在不同事务之间拆分工作可能是实用且有帮助的。

    当由于死锁或锁等待超时而发生事务回滚时,它会取消事务内语句的效果。但如果 start-transaction 语句是 START TRANSACTIONBEGIN 语句,则回滚不会取消该语句。进一步的 SQL 语句成为事务的一部分,直到出现 COMMITROLLBACK 或导致隐式提交的某些 SQL 语句。

  • 如果您没有在语句中指定 IGNORE 选项,则重复键错误会回滚 SQL 语句。

  • row too long error 回滚 SQL 语句。

  • 其他错误大多是被MySQL代码层(InnoDB存储引擎层以上)检测到,并回滚对应的SQL语句。锁不会在单个 SQL 语句的回滚中释放。

【讨论】:

    猜你喜欢
    • 2012-01-25
    • 1970-01-01
    • 1970-01-01
    • 2016-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-17
    • 2014-09-11
    相关资源
    最近更新 更多