事務保證一組 SQL 操作的原子性(全成功或全失敗),是電商訂單、銀行轉賬、庫存扣減等核心業務的基石。本文基於 MySQL 5.6+ / PHP 5.6+,使用 InnoDB 引擎,展示標準事務模式與錯誤自動回滾封裝。
mysqli 或 PDO(本文示例用 mysqli 過程風格,便於理解)。<?php
$link = mysqli_connect('localhost:3307', 'root', 'password', 'web_his');
mysqli_set_charset($link, 'utf8');
// 1. 開啟事務
mysqli_query($link, 'START TRANSACTION') or exit(mysqli_error($link));
// 2. 執行業務 SQL 1
$sql1 = "INSERT INTO pf_item VALUES ('22', '我們', '30')";
if (!mysqli_query($link, $sql1)) {
echo "SQL1 失敗: " . mysqli_error($link) . "
";
mysqli_query($link, 'ROLLBACK') or exit(mysqli_error($link));
exit;
}
// 3. 執行業務 SQL 2
$sql2 = "INSERT INTO pf_item VALUES ('21', 'hell', '10')";
if (!mysqli_query($link, $sql2)) {
echo "SQL2 失敗: " . mysqli_error($link) . "
";
mysqli_query($link, 'ROLLBACK') or exit(mysqli_error($link));
exit;
}
// 4. 全部成功,提交事務
mysqli_query($link, 'COMMIT') or exit(mysqli_error($link));
mysqli_close($link);
echo "事務提交成功";
?>
<?php
/**
* 執行一組 SQL 事務
* @param mysqli $link
* @param array $sqls SQL 語句數組
* @return bool|array 成功返回 true,失敗返回 [false, error_msg, failed_sql]
*/
function executeTransaction(mysqli $link, array $sqls) {
mysqli_query($link, 'START TRANSACTION');
foreach ($sqls as $sql) {
if (!mysqli_query($link, $sql)) {
$err = mysqli_error($link);
mysqli_query($link, 'ROLLBACK');
return [false, $err, $sql];
}
}
mysqli_query($link, 'COMMIT');
return true;
}
// 使用示例
$link = mysqli_connect('localhost', 'root', 'pwd', 'db');
mysqli_set_charset($link, 'utf8');
$res = executeTransaction($link, [
"INSERT INTO orders (user_id, total) VALUES (100, 299.00)",
"INSERT INTO order_items (order_id, product_id, qty) VALUES (LAST_INSERT_ID(), 5, 2)",
"UPDATE products SET stock = stock - 2 WHERE id = 5 AND stock >= 2"
]);
if ($res === true) {
echo "下單成功";
} else {
list(, $err, $sql) = $res;
echo "下單失敗: $err \nSQL: $sql";
}
?>
<?php
$pdo = new PDO('mysql:host=localhost;dbname=db;charset=utf8', 'root', 'pwd', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
try {
$pdo->beginTransaction();
$pdo->exec("INSERT INTO orders (user_id, total) VALUES (100, 299.00)");
$orderId = $pdo->lastInsertId();
$pdo->exec("INSERT INTO order_items (order_id, product_id, qty) VALUES ($orderId, 5, 2)");
$pdo->exec("UPDATE products SET stock = stock - 2 WHERE id = 5 AND stock >= 2");
$pdo->commit();
echo "成功";
} catch (Exception $e) {
$pdo->rollBack();
echo "失敗: " . $e->getMessage();
}
?>
START TRANSACTION / beginTransaction() 會暫時關閉,COMMIT/commit() 恢復。TRUNCATE、LOCK TABLES 會導致隱式提交,事務中避免使用。SQLSTATE[HY000]: General error: 1213 Deadlock 可重試。rollBack() 必被調用,避免事務懸掛。事務是數據一致性的守門員,正確使用模式 + 異常安全封裝,讓核心業務經得起高併發考驗。