需要說明的是,autocommit 不僅會開啟/關閉交易,還會「提交」任何等待中的查詢。
<?php
mysqli_autocommit($link, FALSE); // 關閉自動提交
-一些查詢 1;
-一些查詢 2;
mysqli_commit($link); // 處理目前為止的所有查詢
-一些查詢 3;
-一些查詢 4;
mysqli_autocommit($link, TRUE); // 開啟自動提交
?>
所有 4 個查詢都會被處理。
(PHP 5, PHP 7, PHP 8)
mysqli::autocommit -- mysqli_autocommit — 開啟或關閉資料庫修改的自動提交
物件導向風格
程序風格
開啟或關閉資料庫連線查詢的自動提交模式。
要判斷 autocommit 的目前狀態,請使用 SQL 指令 SELECT @@autocommit
。
如果啟用了 mysqli 錯誤報告 (MYSQLI_REPORT_ERROR
) 且請求的操作失敗,則會產生警告。此外,如果模式設定為 MYSQLI_REPORT_STRICT
,則會改為拋出 mysqli_sql_exception。
範例 #1 mysqli::autocommit() 範例
物件導向風格
<?php
/* Tell mysqli to throw an exception if an error occurs */
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
/* The table engine has to support transactions */
$mysqli->query("CREATE TABLE IF NOT EXISTS language (
Code text NOT NULL,
Speakers int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
/* Turn autocommit off */
$mysqli->autocommit(false);
$result = $mysqli->query("SELECT @@autocommit");
$row = $result->fetch_row();
printf("Autocommit is %s\n", $row[0]);
try {
/* Prepare insert statement */
$stmt = $mysqli->prepare('INSERT INTO language(Code, Speakers) VALUES (?,?)');
$stmt->bind_param('ss', $language_code, $native_speakers);
/* Insert some values */
$language_code = 'DE';
$native_speakers = 50_123_456;
$stmt->execute();
$language_code = 'FR';
$native_speakers = 40_546_321;
$stmt->execute();
/* Commit the data in the database. This doesn't set autocommit=true */
$mysqli->commit();
print "Committed 2 rows in the database\n";
$result = $mysqli->query("SELECT @@autocommit");
$row = $result->fetch_row();
printf("Autocommit is %s\n", $row[0]);
/* Try to insert more values */
$language_code = 'PL';
$native_speakers = 30_555_444;
$stmt->execute();
$language_code = 'DK';
$native_speakers = 5_222_444;
$stmt->execute();
/* Setting autocommit=true will trigger a commit */
$mysqli->autocommit(true);
print "Committed 2 row in the database\n";
} catch (mysqli_sql_exception $exception) {
$mysqli->rollback();
throw $exception;
}
程序風格
<?php
/* Tell mysqli to throw an exception if an error occurs */
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = mysqli_connect("localhost", "my_user", "my_password", "world");
/* The table engine has to support transactions */
mysqli_query($mysqli, "CREATE TABLE IF NOT EXISTS language (
Code text NOT NULL,
Speakers int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
/* Turn autocommit off */
mysqli_autocommit($mysqli, false);
$result = mysqli_query($mysqli, "SELECT @@autocommit");
$row = mysqli_fetch_row($result);
printf("Autocommit is %s\n", $row[0]);
try {
/* Prepare insert statement */
$stmt = mysqli_prepare($mysqli, 'INSERT INTO language(Code, Speakers) VALUES (?,?)');
mysqli_stmt_bind_param($stmt, 'ss', $language_code, $native_speakers);
/* Insert some values */
$language_code = 'DE';
$native_speakers = 50_123_456;
mysqli_stmt_execute($stmt);
$language_code = 'FR';
$native_speakers = 40_546_321;
mysqli_stmt_execute($stmt);
/* Commit the data in the database. This doesn't set autocommit=true */
mysqli_commit($mysqli);
print "Committed 2 rows in the database\n";
$result = mysqli_query($mysqli, "SELECT @@autocommit");
$row = mysqli_fetch_row($result);
printf("Autocommit is %s\n", $row[0]);
/* Try to insert more values */
$language_code = 'PL';
$native_speakers = 30_555_444;
mysqli_stmt_execute($stmt);
$language_code = 'DK';
$native_speakers = 5_222_444;
mysqli_stmt_execute($stmt);
/* Setting autocommit=true will trigger a commit */
mysqli_autocommit($mysqli, true);
print "Committed 2 row in the database\n";
} catch (mysqli_sql_exception $exception) {
mysqli_rollback($mysqli);
throw $exception;
}
以上範例將輸出
Autocommit is 0 Committed 2 rows in the database Autocommit is 0 Committed 2 row in the database Autocommit is 0 Committed 2 rows in the database Autocommit is 0 Committed 2 row in the database
注意:
此函式不適用於非交易式表格類型(例如 MyISAM 或 ISAM)。
需要說明的是,autocommit 不僅會開啟/關閉交易,還會「提交」任何等待中的查詢。
<?php
mysqli_autocommit($link, FALSE); // 關閉自動提交
-一些查詢 1;
-一些查詢 2;
mysqli_commit($link); // 處理目前為止的所有查詢
-一些查詢 3;
-一些查詢 4;
mysqli_autocommit($link, TRUE); // 開啟自動提交
?>
所有 4 個查詢都會被處理。
值得注意的是,您可以使用標準 SQL 執行交易,而無需停用 autocommit。「START TRANSACTION;」將開始一個交易。「COMMIT;」將提交結果,而「ROLLBACK;」將恢復到交易前的狀態。
CREATE TABLE 和 CREATE DATABASE 指令(可能還有其他指令)都會立即提交,而且您的交易看似會終止。因此,之前和之後的任何指令都會被提交,即使後續嘗試了 rollback 也是一樣。
如果您在交易進行中呼叫 mysqli_close(),看起來您會得到隱式 rollback 的功能。
我無法重現下面描述的「程式碼錯誤導致鎖定」問題(我總是成功 rollback,而且腳本可以順利執行無數次)。因此,我認為該問題已在 php-5.2.2 中修復。
我發現如果 PHP 因程式碼錯誤在交易過程中退出,InnoDB 資料表可能會保持鎖定狀態,直到重新啟動 Apache 為止。
簡單的測試方法是透過設定 $mysqli_obj->autocommit(false) 並執行 insert 陳述式來啟動交易。在執行 $mysqli_obj->commit 陳述式之前,讓程式碼錯誤導致 PHP 崩潰。您檢查資料庫,發現沒有插入任何資料(您假設發生了 rollback)…然後您去修復錯誤,並再次嘗試…但這次腳本花了大約 50 秒才逾時 — insert 陳述式返回「1205 - 鎖定等待逾時;請嘗試重新啟動交易」。沒有發生 rollback。而且這個錯誤在您重新啟動 Apache 之前不會消失 — 無論是什麼原因,資源直到進程被終止才會釋放。
我發現使用 `exit` 退出,而不是 PHP 程式碼錯誤,不會造成問題。所以有一個自動 rollback 機制 — 只是當 PHP 意外終止時,它會失效。為了克服程式碼錯誤而必須重新啟動 Apache 是相當激烈的措施。
為了避免這個問題,我在啟動交易時使用「register_shutdown_function()」,並設定一個旗標來表示交易正在進行中(因為沒有 unregister_shutdown_function())。請參見下方。因此,當腳本崩潰時,會呼叫 __shutdown_check() 常式(我認為它需要是公開的)— 它可以呼叫 rollback()。
這些只是一些相關的片段,讓您了解一下…
<?php
public function begin_transaction() {
$ret = $this->mysqli_obj->autocommit(false);
$this->transaction_in_progress = true;
register_shutdown_function(array($this, "__shutdown_check"));
}
public function __shutdown_check() {
if ($this->transaction_in_progress) {
$this->rollback();
}
}
public function commit() {
$ret = $this->mysqli_obj->commit();
$this->transaction_in_progress = false;
}
public function rollback() {
$ret = $this->mysqli_obj->rollback();
$this->transaction_in_progress = false;
}
?>
適用於 PHP 5.1.6 + MySQL 5.0.24a。