6

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

我要做的第一件事是:

$mysqli->autocommit(FALSE);

然后我做了一些查询:

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

然后我通过执行以下操作提交由这 3 个查询组成的事务:

$mysqli->commit();

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

我问这个是因为在我在手册页上看到的评论中: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那么您就不需要回滚,因为事务没有被处理。我对吗?

4

2 回答 2

5

我认为这段代码是错误的,因为如果任何查询失败并且 $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)

你看到'foo'和'bar'的插入是成功的,尽管第二个SQL语句失败了——你甚至可以看到AUTO_INCREMENT-value已经被错误的查询增加了。

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

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

于 2013-07-15T11:11:11.327 回答
4

InnoDB错误处理中所述:

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

  • 如果表空间中的文件空间不足,则会发生MySQLTable is full错误并InnoDB回滚 SQL 语句。

  • 事务死锁导致回滚整个事务InnoDB_ 发生这种情况时重试整个事务。

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

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

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

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

  • Arow too long error回滚 SQL 语句。

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

于 2013-07-15T08:56:04.773 回答