mysqli :: commit& mysqli :: rollback工作?

时间:2013-07-15 08:36:50

标签: php mysql mysqli commit rollback

我已经阅读了在线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那么您不需要执行回滚,因为事务未被处理。我是对的吗?

2 个答案:

答案 0 :(得分:4)

正如InnoDB Error Handling所述:

  

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

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

  •   
  • 交易deadlock会导致InnoDB roll back整个transaction。发生这种情况时重试整个交易。

         

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

         

    繁忙的服务器上的死锁和锁定等待超时都是正常的,应用程序必须知道它们可能会发生并通过重试来处理它们。您可以通过在事务和提交期间对数据的第一次更改之间尽可能少的工作来降低它们的可能性,因此锁可以保持最短的时间和最小的行数。有时在不同交易之间分配工作可能是实际和有用的。

         

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

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

  •   
  • row too long error回滚SQL语句。

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

  •   

答案 1 :(得分:4)

  

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

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

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