事務是資料庫系統區別於檔案系統的核心特性,本文從 ACID 理論、MySQL 實作機制、儲存引擎限制、應用層編程模式四個維度系統闡述。
| 特性 | 英文 | 核心含義 |
|---|---|---|
| 原子性 | Atomicity | 全做或全不做,無中間狀態 |
| 一致性 | Consistency | 資料從一個合法狀態轉移到另一個合法狀態 |
| 隔離性 | Isolation | 並發事務互不干擾,如串列執行 |
| 持久性 | Durability | 提交後永久生效,故障可恢復 |
BEGIN; -- 或 START TRANSACTION;
-- SQL 1
-- SQL 2
-- SQL 3
COMMIT; -- 全部成功
-- 或
ROLLBACK; -- 任一失敗,全部撤銷
SET AUTOCOMMIT = 0;
-- 後續所有 SQL 同屬一事務
COMMIT; -- 確認
-- 或
ROLLBACK; -- 回滾
SET AUTOCOMMIT = 1; -- 恢復預設
注意: AUTOCOMMIT=0 影響整個連線生命週期,易造成長事務、鎖爭用,建議僅在明確需要的程式碼區塊使用方式 1。
-- 檢查引擎支援
SHOW VARIABLES LIKE 'have_innodb';
-- 建表必須指定
CREATE TABLE t (id INT) ENGINE=InnoDB;
MyISAM、Memory、CSV 等引擎不支援事務,操作即時生效,無法回滾。
mysql> USE test;
mysql> CREATE TABLE dbtest (id INT) ENGINE=InnoDB;
-- 成功提交
mysql> BEGIN;
mysql> INSERT INTO dbtest VALUES (5), (6);
mysql> COMMIT;
mysql> SELECT * FROM dbtest; -- 5, 6 存在
-- 回滾驗證
mysql> BEGIN;
mysql> INSERT INTO dbtest VALUES (7);
mysql> ROLLBACK;
mysql> SELECT * FROM dbtest; -- 僅 5, 6,7 已回滾
function executeTransaction(PDO $pdo, array $sqls): bool {
$pdo->beginTransaction();
try {
foreach ($sqls as $sql) {
$pdo->exec($sql); // 或 prepare/execute
}
$pdo->commit();
return true;
} catch (Exception $e) {
$pdo->rollBack();
error_log("Transaction failed: " . $e->getMessage());
return false;
}
}
// 使用
$pdo = new PDO("mysql:host=localhost;dbname=task", "root", "");
$success = executeTransaction($pdo, [
"INSERT INTO trans (id) VALUES (2)",
"INSERT INTO trans (id) VALUES (4)"
]);
$link = mysql_connect("localhost", "root", "");
mysql_select_db("task");
mysql_query("SET AUTOCOMMIT=0");
mysql_query("BEGIN");
$ok = true;
$ok &= mysql_query("INSERT INTO trans (id) VALUES ('2')");
$ok &= mysql_query("INSERT INTO trans (id) VALUES ('4')");
if ($ok) mysql_query("COMMIT"); else mysql_query("ROLLBACK");
mysql_close($link);
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false);
// 業務邏輯
try (PreparedStatement ps1 = conn.prepareStatement("INSERT INTO t1...")) { ps1.executeUpdate(); }
try (PreparedStatement ps2 = conn.prepareStatement("UPDATE t2...")) { ps2.executeUpdate(); }
conn.commit();
} catch (SQLException e) {
conn.rollback();
throw new BusinessException("事務失敗", e);
} finally {
conn.setAutoCommit(true); // 歸還連線池前恢復
conn.close();
}
pt-online-schema-change 類工具ER_LOCK_DEADLOCK 重試