事務確保一組操作「要么全成功,要么全失敗」,是金融、庫存、訂單等強一致性場景的基石。本文以經典銀行轉賬為例,講解 mysqli 事務 API、ACID 特性落地、死鎖處理與最佳實踐。
$mysqli = new mysqli('localhost', 'user', 'pass', 'bank');
$mysqli->set_charset('utf8mb4');
// 1. 關閉自動提交,開啟事務
$mysqli->autocommit(false);
// 等價於:$mysqli->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);
try {
// 2. 扣款
$sql1 = "UPDATE accounts SET balance = balance - 200 WHERE id = 1 AND balance >= 200";
if (!$mysqli->query($sql1) || $mysqli->affected_rows === 0) {
throw new Exception('Insufficient balance or account not found');
}
// 3. 入賬
$sql2 = "UPDATE accounts SET balance = balance + 200 WHERE id = 2";
if (!$mysqli->query($sql2)) {
throw new Exception('Credit failed: ' . $mysqli->error);
}
// 4. 提交事務
$mysqli->commit();
echo "Transfer succeeded.\n";
} catch (Exception $e) {
// 5. 回滾事務
$mysqli->rollback();
echo "Transfer failed: " . $e->getMessage() . "\n";
} finally {
$mysqli->autocommit(true); // 恢復自動提交模式
$mysqli->close();
}
| 特性 | 實現機制 |
|---|---|
| Atomicity (原子性) | Undo Log 記錄舊值,回滾時反向恢復。 |
| Consistency (一致性) | 外鍵、唯一索引、CHECK 約束 + 應用層邏輯保證。 |
| Isolation (隔離性) | MVCC + 行鎖(Record Lock、Gap Lock、Next-Key Lock),四級隔離級別(READ UNCOMMITTED ~ SERIALIZABLE)。 |
| Durability (持久性) | Redo Log (WAL) + Doublewrite Buffer + fsync 策略 (innodb_flush_log_at_trx_commit=1)。 |
// 設置事務隔離級別(需在 begin_transaction 前或 autocommit=false 後)
$mysqli->query("SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ");
// 或啟動時指定(mysqli 5.6+)
$mysqli->begin_transaction(MYSQLI_TRANS_START_READ_WRITE | MYSQLI_TRANS_START_WITH_CONSISTENT_SNAPSHOT);
innodb_lock_wait_timeout(默認 50s),超時返回錯誤碼 1205。// 重試邏輯示例
$maxRetries = 3;
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
try {
// ... 事務邏輯 ...
break; // 成功跳出
} catch (mysqli_sql_exception $e) {
if ($e->getCode() === 1213 || $e->getCode() === 1205) {
if ($attempt === $maxRetries) throw $e;
usleep(50000 * $attempt + random_int(0, 10000)); // 50ms, 100ms, 150ms + 抖動
continue;
}
throw $e; // 非鎖錯誤直接拋出
}
}
LOCK TABLES。long_query_time 並記錄 Trx_id 關聯業務追蹤。事務不是萬能藥,但用對了就是數據一致性的最後防線。理解 InnoDB 鎖機制、死鎖重試、隔離級別選擇,才能寫出經得起高併發考驗的金融級代碼。