[英]How does mysqli::commit & mysqli::rollback work?
我已經閱讀了在線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
那么您不需要執行回滾,因為事務未被處理。 我對嗎?
InnoDB
錯誤處理並不總是與SQL標准中指定的相同。 根據該標准,SQL語句中的任何錯誤都應導致該語句的回滾。InnoDB
有時只會回滾部分語句或整個事務。 以下各項描述了InnoDB
如何執行錯誤處理:
如果表空間中的文件空間不足,則會發生MySQL
Table is full
錯誤,並且InnoDB
回滾SQL語句。事務死鎖導致
InnoDB
回滾整個事務 。 發生這種情況時重試整個事務。鎖定等待超時會導致
InnoDB
僅回滾等待鎖定並遇到超時的單個語句。 (要使整個事務回滾,請使用--innodb_rollback_on_timeout
選項啟動服務器。)如果使用當前行為,則重試該語句,如果使用--innodb_rollback_on_timeout
,則重試整個事務。在繁忙的服務器上,死鎖和鎖定等待超時都是正常的,應用程序必須知道它們可能會發生並通過重試來處理它們。 您可以通過在事務和提交期間對數據的第一次更改之間盡可能少的工作來降低它們的可能性,因此鎖可以保持最短的時間和最小的行數。 有時,在不同交易之間分配工作可能是實際且有用的。
當由於死鎖或鎖等待超時而發生事務回滾時,它會取消事務中語句的效果。 但是,如果start-transaction語句是
START TRANSACTION
或BEGIN
語句,則回滾不會取消該語句。 在發生COMMIT
,ROLLBACK
或導致隱式提交的某些SQL語句之前,其他SQL語句將成為事務的一部分。如果未在語句中指定
IGNORE
選項,則重復鍵錯誤將回滾SQL語句。
row too long error
回滾SQL語句。其他錯誤主要由MySQL代碼層(在
InnoDB
存儲引擎級別之上)檢測到,並且它們回滾相應的SQL語句。 鎖定不會在單個SQL語句的回滾中釋放。
我認為這段代碼是錯誤的,因為如果任何查詢失敗並且$ 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特有的,其他存儲引擎可能以不同的方式處理這些錯誤)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.