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

PHP + MySQLi 事務處理實戰:轉賬系統完整實作

     編輯:PHP 相關知識

資料庫事務是確保資料一致性的核心機制。本文以經典轉賬場景為例,詳細展示如何使用 PHP MySQLi 實現事務的提交與回滾,包含完整程式碼、關鍵步驟解析與生產環境最佳實踐。

事務處理核心概念

事務遵循 ACID 原則:

  • 原子性:操作要麼全成功,要麼全失敗
  • 一致性:資料從一個一致狀態轉移到另一個
  • 隔離性:並發事務互不干擾
  • 持久性:提交後永久生效

完整轉賬實作程式碼

<?php
header("Content-Type: text/html; charset=utf-8");

// 1. 建立資料庫連線
$mysqli = new mysqli("localhost", "root", "064319", "php");
$mysqli->set_charset("utf8");

if ($mysqli->connect_errno) {
    die('資料庫連線失敗: ' . $mysqli->connect_error);
}

// 2. 關閉自動提交,開啟事務
$mysqli->autocommit(false);
$flag = true; // 事務執行狀態標記

try {
    // 3. 扣款操作(轉出帳戶)
    $query = "UPDATE account SET balance = balance - 1000 WHERE id = 3";
    $result = $mysqli->query($query);
    
    if (!$result || $mysqli->affected_rows === 0) {
        throw new Exception("扣款失敗:帳戶 3 餘額不足或不存在");
    }
    
    // 4. 存款操作(轉入帳戶)
    $query = "UPDATE account SET balance = balance + 1000 WHERE id = 2";
    $result = $mysqli->query($query);
    
    if (!$result || $mysqli->affected_rows === 0) {
        throw new Exception("存款失敗:帳戶 2 不存在");
    }
    
    // 5. 所有操作成功,提交事務
    $mysqli->commit();
    echo '轉賬成功';
    
} catch (Exception $e) {
    // 6. 任一操作失敗,回滾事務
    $mysqli->rollback();
    echo '轉賬失敗: ' . $e->getMessage();
} finally {
    // 7. 恢復自動提交模式並關閉連線
    $mysqli->autocommit(true);
    $mysqli->close();
}
?>

關鍵步驟深度解析

1. 關閉自動提交模式

$mysqli->autocommit(false);

預設 MySQLi 自動提交每條 SQL。關閉後,需手動呼叫 commit() 或 rollback()。

2. 狀態標記與異常處理

使用 $flag 或 try-catch 追蹤操作狀態。現代 PHP 建議使用異常處理,程式碼更清晰。

3. 影響行數檢查

if (!$result || $mysqli->affected_rows === 0)

雙重檢查:SQL 執行成功且實際影響資料行。防止 WHERE 條件不匹配導致邏輯錯誤。

4. 提交與回滾

  • commit():永久套用所有變更
  • rollback():撤銷事務內所有變更

進階最佳實踐

1. 鎖機制與並發控制

// 為防止並發修改同一帳戶,建議加鎖查詢
$query = "SELECT balance FROM account WHERE id = 3 FOR UPDATE";

FOR UPDATE 在事務中鎖定該行,防止髒讀與更新衝突。

2. 存儲過程封裝

複雜業務邏輯建議移至存儲過程:

CREATE PROCEDURE transfer_account(
    IN from_id INT, IN to_id INT, IN amount DECIMAL(10,2)
)
BEGIN
    DECLARE from_balance DECIMAL(10,2);
    
    START TRANSACTION;
    
    SELECT balance INTO from_balance FROM account WHERE id = from_id FOR UPDATE;
    IF from_balance < amount THEN
        ROLLBACK;
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '餘額不足';
    END IF;
    
    UPDATE account SET balance = balance - amount WHERE id = from_id;
    UPDATE account SET balance = balance + amount WHERE id = to_id;
    
    COMMIT;
END

3. 死鎖檢測與重試機制

function executeWithRetry($mysqli, $callback, $maxRetries = 3) {
    for ($i = 0; $i < $maxRetries; $i++) {
        try {
            return $callback($mysqli);
        } catch (mysqli_sql_exception $e) {
            if ($e->getCode() === 1213) { // Deadlock
                usleep(100000 * ($i + 1)); // 指數退避
                continue;
            }
            throw $e;
        }
    }
    throw new Exception("重試次數耗盡");
}

常見錯誤與避坑指南

錯誤類型原因解決方案
忘記關閉 autocommit單條 SQL 即時生效,無法回滾事務開始前必須 $mysqli->autocommit(false)
未檢查 affected_rowsWHERE 條件不匹配不報錯但邏輯失敗每次寫入操作後檢查 affected_rows > 0
事務過長鎖持有時間長,降低併發效能事務內僅放核心寫操作,讀操作移至外部
異常未回滾PHP 異常中斷流程,事務懸掛使用 try-catch-finally 確保 rollback/commit

總結

PHP MySQLi 事務處理透過 autocommit(false)、commit()、rollback() 三大核心方法,配合嚴謹的錯誤檢查與異常處理,能可靠實現資料一致性保障。轉賬場景只是冰山一角,訂單處理、庫存扣減、積分變更等所有涉及多步寫入的業務都應納入事務管理。掌握事務設計模式與進階優化技巧,是構建高可靠系統的關鍵能力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved