論壇扣款、電商下單等業務中,若扣款成功但記錄寫入失敗(斷網、死機、停電),將造成資料不一致。MySQL 事務回滾是解決此類問題的標準方案。本文從引擎確認、表轉換到 PHP 代碼落地,提供完整實戰指南。
**僅 InnoDB/BDB 支援事務**。若現有表為 MyISAM,需轉換:
MySQL-Front 等 GUI 工具修改表類型。ALTER TABLE tablename ENGINE=InnoDB;(TYPE=InnoDB 已棄用)。<?php
$conn = mysqli_connect('localhost', 'root', 'root', 'dbname');
mysqli_set_charset($conn, 'utf8mb4');
// 1. 開啟事務
mysqli_begin_transaction($conn); // 推薦 mysqli 5.5+,或 mysqli_query($conn, "START TRANSACTION");
// 2. 業務 SQL
$sql1 = "INSERT INTO `account_log` (`user_id`, `amount`) VALUES (101, -50)";
$sql2 = "INSERT INTO `order` (`user_id`, `total`) VALUES (101, 50)"; // 假設此處可能失敗
$res1 = mysqli_query($conn, $sql1);
$res2 = mysqli_query($conn, $sql2);
// 3. 統一判斷提交或回滾
if ($res1 && $res2) {
mysqli_commit($conn);
echo '交易提交成功。';
} else {
mysqli_rollback($conn);
echo '交易回滾:' . mysqli_error($conn);
}
mysqli_close($conn);
?>
COMMIT 前所有操作對其他連線不可見;ROLLBACK 如操作從未發生。try-catch 包裹並配合 PDO,自動處理異常回滾。innodb_lock_wait_timeout 設定,避免長事務阻塞。ALTER TABLE 等)會導致隱式提交。<?php
$pdo = new PDO("mysql:host=localhost;dbname=test;charset=utf8mb4", 'root', 'root', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
try {
$pdo->beginTransaction();
$pdo->exec("INSERT INTO `account_log` (`user_id`, `amount`) VALUES (101, -50)");
$pdo->exec("INSERT INTO `order` (`user_id`, `total`) VALUES (101, 50)");
$pdo->commit();
echo '成功';
} catch (Exception $e) {
$pdo->rollBack();
echo '失敗回滾:' . $e->getMessage();
}
?>
事務回滾雖簡單,但需嚴守「InnoDB 前提、原子操作、異常必捕」三原則,才能在災難發生時守住資料底線。