首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP MySQL 事務處理完整實戰:ACID 原則、巢狀陷阱、PDO 現代寫法

     編輯:PHP 相關知識

事務四大特性 (ACID)

特性說明
Atomicity (原子性)全部成功或全部失敗,無中間狀態
Consistency (一致性)資料從一個合法狀態變換到另一個合法狀態
Isolation (隔離性)並發事務互不干擾 (READ COMMITTED 等級別)
Durability (持久性)Commit 後寫入磁碟,斷電不丟失

前置條件

  • 表引擎必須為 InnoDB (MyISAM 不支援事務)
  • 單一連線內執行 (連線池需確保同一連線)

傳統 mysql_* 寫法 (已棄用,僅供維護舊專案參考)

<?php
$conn = mysql_connect('localhost', 'root', '');
mysql_select_db('test', $conn);
mysql_query("SET NAMES utf8");

mysql_query('START TRANSACTION');
$ok = true;

$ok &= mysql_query("INSERT INTO t1 (name) VALUES ('A')");
$ok &= mysql_query("INSERT INTO t2 (name) VALUES ('B')"); // 假設這裡失敗

if ($ok) {
    mysql_query('COMMIT');
} else {
    mysql_query('ROLLBACK');
}
mysql_close($conn);

現代 PDO 寫法 (推薦)

<?php
$dsn = 'mysql:host=localhost;dbname=test;charset=utf8mb4';
$pdo = new PDO($dsn, 'user', 'pass', [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false, // 使用原生預處理
]);

try {
    $pdo->beginTransaction();

    $stmt = $pdo->prepare("INSERT INTO t1 (name) VALUES (?)");
    $stmt->execute(['A']);

    $stmt = $pdo->prepare("INSERT INTO t2 (name) VALUES (?)");
    $stmt->execute(['B']); // 失敗會拋出 PDOException

    $pdo->commit();
    echo "事務提交成功";
} catch (Throwable $e) {
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    echo "事務回滾: " . $e->getMessage();
}

關鍵陷阱與最佳實踐

1. 隱含提交

  • DDL 語句 (CREATE, ALTER, DROP, TRUNCATE) 會隱含提交當前事務。
  • 事務中避免執行 DDL。

2. 巢狀事務模擬 (Savepoint)

MySQL 不支援真正巢狀事務,第二個 beginTransaction() 會隱含提交第一個。需用 Savepoint:

<?php
$pdo->beginTransaction(); // 外層
try {
    $pdo->exec('SAVEPOINT sp1'); // 內層起點
    $pdo->exec("INSERT INTO t3 VALUES ('C')");
    // 內層邏輯失敗
    throw new Exception('Inner fail');
    $pdo->exec('RELEASE SAVEPOINT sp1'); // 內層成功
} catch (Throwable) {
    $pdo->exec('ROLLBACK TO SAVEPOINT sp1'); // 僅回滾內層
}
// 外層繼續...
$pdo->commit();

3. 死鎖處理

<?php
$maxRetries = 3;
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
    try {
        $pdo->beginTransaction();
        // ... 業務邏輯 ...
        $pdo->commit();
        break;
    } catch (PDOException $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        if ($e->getCode() === 'HY000' && strpos($e->getMessage(), 'Deadlock') !== false) {
            if ($attempt === $maxRetries) throw $e;
            usleep(100000 * $attempt); // 指數退避
            continue;
        }
        throw $e;
    }
}

4. 長事務風險

  • 鎖定資源久 → 並發下降
  • Undo Log 膨脹
  • 主從複寫延遲
  • 原則:事務內只做必要的寫入,業務邏輯、外部 API 呼叫、檔案操作移出事務。

隔離等級與鎖

<?php
// 設定當前事務隔離等級
$pdo->exec("SET TRANSACTION ISOLATION LEVEL READ COMMITTED");
$pdo->beginTransaction();

// 或啟動時指定 (MySQL 8.0+)
$pdo->beginTransaction(PDO::TRANSACTION_READ_COMMITTED);
等級髒讀不可重複讀幻讀鎖行為
READ UNCOMMITTED可能可能可能無共享鎖
READ COMMITTED (預設)否可能可能讀取瞬間鎖
REPEATABLE READ否否可能 (InnoDB 用 Next-Key Lock 解決)首次讀取鎖定範圍
SERIALIZABLE否否否全表鎖/範圍鎖

總結檢查清單

  1. 表引擎 = InnoDB
  2. 使用 PDO + Exception 模式
  3. 所有寫入用預處理
  4. 事務內只做 DB 寫入
  5. 捕獲例外時檢查 inTransaction() 再 rollBack()
  6. 死鎖重試機制
  7. 避免 DDL、避免長事務
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved