首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

mysqli 事務控制陷阱:變量拼寫錯誤與存儲引擎不匹配導致回滾失效

     編輯:MySQL 資料庫相關知識

PHP 使用 mysqli 實現資料庫事務時,常因細節疏忽導致「事務不生效、部分 SQL 提交成功」的假象。本文通過一個真實排查案例,揭示變量拼寫錯誤、缺少顯式開啟事務、表引擎非 InnoDB 三大核心坑點。

一、初版問題代碼

public static function exec_sql_tran($arr_sql) {
    $result = true;
    $con = new MySQLi(DBHOST, DB_USER, DB_PASSWD, DB_NAME);
    $con->autocommit(FALSE);

    foreach ($arr_sql as $key => $sql) {
        if (!$con->query($sql)) {
            echo $con->error . "\n";
            $resulf = false;  // ★ 拼寫錯誤:$resulf ≠ $result
            $con->rollback();
            break;
        }
    }

    if ($result) { $con->commit(); }
    else { $con->rollback(); }
    $con->close();
}

現象: 兩條 INSERT,第一條正確、第二條報錯,但第一條數據仍入庫。

根因: $resulf = false 未修改 $result,導致後續判斷走入 commit() 分支。

二、修正變量後仍失效

$result = false;  // 已修正拼寫
break;
// ...
if ($result) { $con->commit(); } else { $con->rollback(); }

現象: SQL1 正確、SQL2 錯誤 → SQL1 仍入庫;SQL1 錯誤、SQL2 正確 → 兩條均不入庫(因 break 終止循環)。

分析: break 只是跳出循環,並未阻止已執行的 SQL 被隱式提交。mysqli 默認 autocommit=FALSE 只影響後續語句,若未顯式 BEGIN,首條語句可能仍以自動提交模式執行。

三、補全顯式事務邊界

$con->autocommit(false);
$con->begin_transaction();  // PHP 5.5+ 專用
// 或兼容寫法:$con->query('START TRANSACTION');

若 PHP 版本 < 5.5,begin_transaction() 不存在,必須用 query('START TRANSACTION') 或 query('BEGIN')。

四、終極兇手:表引擎非 InnoDB

經反複驗證代碼邏輯無誤後,排查發現目標表引擎為 MyISAM。

SHOW TABLE STATUS LIKE 'target_table'\G
-- Engine: MyISAM  → 不支持事務!

解決: ALTER TABLE target_table ENGINE=InnoDB; 後事務行為恢復正常。

五、完整最佳實踐模板

public static function exec_sql_tran(array $arr_sql): bool {
    $con = new MySQLi(DBHOST, DB_USER, DB_PASSWD, DB_NAME);
    if ($con->connect_errno) {
        error_log("MySQLi Connect Error: {$con->connect_error}");
        return false;
    }

    $con->autocommit(false);
    $con->query('START TRANSACTION');  // 兼容所有 PHP 版本

    try {
        foreach ($arr_sql as $sql) {
            if (!$con->query($sql)) {
                throw new Exception($con->error);
            }
        }
        $con->commit();
        return true;
    } catch (Exception $e) {
        $con->rollback();
        error_log("Transaction Rolled Back: {$e->getMessage()}");
        return false;
    } finally {
        $con->close();
    }
}

六、避坑檢查清單

  • ✅ 變量名拼寫一致($result 而非 $resulf)
  • ✅ 顯式調用 START TRANSACTION / BEGIN / begin_transaction()
  • ✅ 確認 所有參與表均為 InnoDB(SHOW TABLE STATUS)
  • ✅ 異常分支務必 rollback() 並 close() 連接
  • ✅ 生產環境建議啟用 mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) 拋出異常

事務控制看似簡單,實則暗藏「語法陷阱、版本差異、引擎前提」三重門檻。逐項驗證清單,方能確保 ACID 特性真正落地。

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