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

PHP PDO 事務處理完整指南:原子性操作與錯誤回滾實戰

     編輯:PHP 綜合知識

PHP PDO 事務處理核心機制與實戰應用

事務具備 ACID 四大特性(原子性、一致性、隔離性、持久性)。PDO 為支援事務的資料庫提供完整介面,本文詳細說明事務配置、操作流程與最佳實踐。

事務核心三步驟

// 1. 關閉自動提交(預設開啟)
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, false);

// 2. 開啟事務
$pdo->beginTransaction();

// 3. 提交或回滾
$pdo->commit();     // 全部成功
$pdo->rollback();   // 任一失敗

標準事務處理模板

<?php
try {
    $pdo->beginTransaction(); // 開啟事務
    
    // 第一步操作
    $row = $pdo->exec("INSERT INTO orders (user_id, total) VALUES (1, 100)");
    if (!$row) throw new PDOException('訂單建立失敗');
    
    $orderId = $pdo->lastInsertId();
    
    // 第二步操作
    $row = $pdo->exec("INSERT INTO order_items (order_id, product_id, qty) VALUES ($orderId, 5, 2)");
    if (!$row) throw new PDOException('訂單明細建立失敗');
    
    // 第三步操作:扣減庫存
    $row = $pdo->exec("UPDATE products SET stock = stock - 2 WHERE id = 5");
    if (!$row) throw new PDOException('庫存扣減失敗');
    
    // 全部成功,提交事務
    $pdo->commit();
    echo "訂單處理成功";
    
} catch (PDOException $e) {
    // 任一失敗,回滾事務
    $pdo->rollback();
    exit('事務失敗:' . $e->getMessage());
}
?>

關鍵注意事項

項目說明
資料庫引擎必須使用 InnoDB,MyISAM 不支援事務
自動提交事務期間必須關閉 ATTR_AUTOCOMMIT
DDL 語句CREATE/DROP/ALTER 會隱式提交,不可在事務中使用
SELECT 鎖定使用 SELECT ... FOR UPDATE 避免幻讀
巢狀事務PDO 不支援巢狀事務,需使用 Savepoint

Savepoint 實作(巢狀事務替代方案)

<?php
$pdo->beginTransaction();

try {
    $pdo->exec("INSERT INTO a VALUES (1)");
    
    // 建立儲存點
    $pdo->exec("SAVEPOINT sp1");
    
    try {
        $pdo->exec("INSERT INTO b VALUES (2)");
        // 子操作失敗時
        throw new Exception("子操作失敗");
    } catch (Exception $e) {
        // 僅回滾到儲存點
        $pdo->exec("ROLLBACK TO SAVEPOINT sp1");
        echo "子操作已回滾,主事務繼續";
    }
    
    $pdo->exec("INSERT INTO c VALUES (3)");
    $pdo->commit();
    
} catch (Exception $e) {
    $pdo->rollback();
}
?>

隔離等級設定

// 設定隔離等級(需在 beginTransaction 前)
$pdo->exec("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED");

/* 隔離等級對照:
READ UNCOMMITTED  - 讀未提交(髒讀)
READ COMMITTED    - 讀已提交(不可重複讀)★ 預設
REPEATABLE READ   - 可重複讀(幻讀)★ MySQL 預設
SERIALIZABLE      - 序列化(效能最低)
*/

實戰案例:轉帳交易

<?php
function transfer($pdo, $from, $to, $amount) {
    try {
        $pdo->beginTransaction();
        
        // 檢查餘額(加鎖防併發)
        $stmt = $pdo->prepare("SELECT balance FROM accounts WHERE id = ? FOR UPDATE");
        $stmt->execute([$from]);
        $balance = $stmt->fetchColumn();
        
        if ($balance < $amount) {
            throw new Exception("餘額不足");
        }
        
        // 扣款
        $pdo->exec("UPDATE accounts SET balance = balance - $amount WHERE id = $from");
        
        // 入帳
        $pdo->exec("UPDATE accounts SET balance = balance + $amount WHERE id = $to");
        
        // 記錄流水
        $pdo->exec("INSERT INTO transactions (from_id, to_id, amount) VALUES ($from, $to, $amount)");
        
        $pdo->commit();
        return true;
        
    } catch (Exception $e) {
        $pdo->rollback();
        error_log("轉帳失敗: " . $e->getMessage());
        return false;
    }
}
?>

常見錯誤與除錯

  1. 忘記關閉自動提交:導致每條 SQL 獨立提交,事務失效
  2. catch 區塊未回滾:異常發生時資料庫處於不一致狀態
  3. 在事務中執行 DDL:隱式提交破壞事務完整性
  4. 長事務鎖定資源:造成併發阻塞,建議拆分業務邏輯
  5. 未處理死鎖:MySQL 會自動回滾其中一筆,需捕獲錯誤碼 1213 重試

效能優化建議

  • 縮短事務範圍:只包含必要的寫入操作
  • 避免事務中進行網路請求、檔案 I/O、複雜運算
  • 批次操作使用單一事務而非多筆事務
  • 高併發場景考慮樂觀鎖(version 欄位)替代悲觀鎖
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved