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

MySQL 事務處理完整指南:ACID、鎖模式、隔離級別與 PHP 實戰

     編輯:MySQL 綜合教學

事務是數據庫併發控制與數據一致性的基石。MySQL 僅 InnoDB 支援事務。本文系統講解 ACID 原理、事務控制語句、鎖模式、隔離級別、死鎖處理及 PHP 封裝實戰。

核心前提

  • 表引擎必須為 InnoDB:CREATE TABLE t (...) ENGINE=InnoDB
  • 驗證:SHOW VARIABLES LIKE 'have_innodb'; → YES

ACID 四大特性

特性含義MySQL 實現
原子性全做或全不做Undo Log 回滾
一致性約束不被破壞外鍵、觸發器、應用邏輯
隔離性併發事務互不干擾MVCC + 鎖(4 隔離級別)
持久性提交後永久生效Redo Log (WAL) + 雙寫緩衝

事務控制語句

-- 開啟
START TRANSACTION;  -- 或 BEGIN

-- 提交
COMMIT;

-- 回滾
ROLLBACK;

-- 保存點
SAVEPOINT sp1;
ROLLBACK TO SAVEPOINT sp1;  -- 回滾到保存點
RELEASE SAVEPOINT sp1;

自動提交模式

SET AUTOCOMMIT = 0;  -- 關閉,後續語句同屬一事務,直到 COMMIT/ROLLBACK
SET AUTOCOMMIT = 1;  -- 開啟(默認),每條語句隱式提交

鎖模式(InnoDB 行鎖)

模式SQL兼容性場景
共享鎖 (S)SELECT ... LOCK IN SHARE MODE兼容 S,阻塞 X讀取需防止被修改
排他鎖 (X)SELECT ... FOR UPDATE
UPDATE/DELETE/INSERT
阻塞 S、X修改前確認狀態
意向鎖 (IS/IX)隱式加於表級—多粒度鎖協協調

關鍵:索引與行鎖

-- 命中索引 → 行鎖
SELECT * FROM t WHERE uid = 'xxx' FOR UPDATE;

-- 未命中索引 → 表鎖(全表掃描加 X 鎖)
SELECT * FROM t WHERE unindexed_col = 'xxx' FOR UPDATE;

隔離級別

級別臟讀不可重複讀幻讀鎖策略適用場景
READ UNCOMMITTEDYYY無 S 鎖極少用
READ COMMITTEDNYY行鎖(無間隙鎖)高併發、語句級一致性
REPEATABLE READ (默認)NNN*Next-Key Lock一般業務、事務級一致性
SERIALIZABLENNN表級鎖/全共享鎖極高一致性要求

* RR 級別通過 Next-Key Lock(行鎖+間隙鎖)解決幻讀。

設置隔離級別

-- 下一事務生效
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 全局新連接生效
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

死鎖處理

  • InnoDB 自動檢測,回滾代價較小的事務(修改行數少)
  • 應用捕獲 ERROR 1213 (HY000): Deadlock found 重試
  • innodb_lock_wait_timeout (默認 50s) 控制等待超時
  • 預防:統一加鎖順序、縮短事務、避免長事務

PHP 事務封裝實戰

mysqli 版

<?php
function executeTransaction(mysqli $mysqli, array $sqls) {
    $mysqli->autocommit(false);
    try {
        foreach ($sqls as $sql) {
            if (!$mysqli->query($sql)) {
                throw new Exception($mysqli->error);
            }
        }
        $mysqli->commit();
        return true;
    } catch (Exception $e) {
        $mysqli->rollback();
        error_log('Transaction rolled back: ' . $e->getMessage());
        return false;
    } finally {
        $mysqli->autocommit(true);
    }
}

// 使用
$mysqli = new mysqli('localhost', 'user', 'pass', 'db');
$mysqli->set_charset('utf8mb4');

$ok = executeTransaction($mysqli, [
    "UPDATE accounts SET balance = balance - 100 WHERE id = 1",
    "UPDATE accounts SET balance = balance + 100 WHERE id = 2",
    "INSERT INTO transfers (from_id, to_id, amount) VALUES (1, 2, 100)"
]);
$mysqli->close();

PDO 版(推薦)

<?php
function pdoTransaction(PDO $pdo, callable $callback) {
    $pdo->beginTransaction();
    try {
        $result = $callback($pdo);
        $pdo->commit();
        return $result;
    } catch (Exception $e) {
        $pdo->rollBack();
        throw $e;
    }
}

// 使用
$pdo = new PDO('mysql:host=localhost;dbname=db;charset=utf8mb4', 'user', 'pass', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);

pdoTransaction($pdo, function($pdo) {
    $pdo->exec("UPDATE accounts SET balance = balance - 100 WHERE id = 1");
    $pdo->exec("UPDATE accounts SET balance = balance + 100 WHERE id = 2");
    $pdo->prepare("INSERT INTO transfers (from_id, to_id, amount) VALUES (?, ?, ?)")
      ->execute([1, 2, 100]);
});

實戰案例:庫存扣減

<?php
// 正確:SELECT FOR UPDATE 加排他鎖,防超賣
$pdo->beginTransaction();
$stmt = $pdo->prepare("SELECT stock FROM goods WHERE id = ? FOR UPDATE");
$stmt->execute([$goodsId]);
$stock = $stmt->fetchColumn();

if ($stock > 0) {
    $pdo->prepare("UPDATE goods SET stock = stock - 1 WHERE id = ?")->execute([$goodsId]);
    $pdo->prepare("INSERT INTO orders (goods_id, user_id) VALUES (?, ?)")->execute([$goodsId, $userId]);
    $pdo->commit();
    echo '下單成功';
} else {
    $pdo->rollBack();
    echo '庫存不足';
}

總結

MySQL 事務核心是 InnoDB + ACID。實戰要點:選對隔離級別(默認 RR 通常足夠)、用好 SELECT FOR UPDATE 防併發、縮短事務範圍、統一加鎖順序防死鎖、PDO 封裝事務邏輯。金融、庫存、訂單等核心業務必須使用事務保證數據正確性。

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