mysqli::commit&;mysqli::回滚工作


How does mysqli::commit & mysqli::rollback work?

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

我要做的第一件事是:

$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,那么您就不需要进行回滚,因为事务没有得到处理。我说得对吗?

我认为此代码是错误的,因为如果任何查询失败并且$all_query_ok==false,则不需要执行回滚,因为未处理事务。我说得对吗?

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

如果单个SQL语句失败,则会回滚语句(如@eggyal的Answer中所述),但事务仍处于打开状态。如果现在调用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"answers"bar"的插入是成功的,尽管第二条SQL语句失败了——您甚至可以看到AUTO_INCREMENT-值因错误的查询而增加。

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

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

InnoDB错误处理:中所述

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

  • 如果表空间中的文件空间用完,则会发生MySQL Table is full错误,InnoDB会回滚SQL语句。

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

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

    死锁和锁等待超时在繁忙的服务器上都是正常的,应用程序有必要意识到它们可能会发生,并通过重试来处理它们。您可以通过在事务期间对数据的第一次更改和提交之间尽可能少地执行工作来降低锁定的可能性,这样锁定的时间就尽可能短,行数也尽可能少。有时,在不同的事务之间划分工作可能既实用又有用。

    当事务回滚由于死锁或锁等待超时而发生时,它会取消事务中语句的效果。但是,如果启动事务语句是START TRANSACTIONBEGIN语句,回滚不会取消该语句。在COMMITROLLBACK或某些导致隐式提交的SQL语句出现之前,其他SQL语句将成为事务的一部分。

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

  • row too long error回滚SQL语句。

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