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

MySQL 事務 ACID 特性深度解析:從銀行轉賬到 SELECT FOR UPDATE 鎖機制實戰

     編輯:MySQL 綜合教學

MySQL 事務 ACID 特性深度解析

事務是資料庫區別於文件系統的核心特性。MySQL 中僅 **InnoDB** 和 **BDB** (已棄用) 支援事務。本文以 InnoDB 為例,結合「銀行轉賬」、「網上搶書」經典案例,深度講解 ACID 四大特性、隔離級別、鎖機制與 PHP 實戰代碼。

ACID 四大特性 (以銀行轉賬為例)

特性含義轉賬場景體現
原子性事務不可分割,全做或全不做扣款與入賬必須同時成功/失敗
一致性事務前後資料庫滿足所有約束總金額守恆、賬戶餘額非負
隔離性併發事務互不干擾,可序列化A 轉賬未提交時,B 查詢見不到中間狀態
持久性提交後永久保存,抗故障系統崩潰重啟後轉賬記錄不丟失

經典併發問題:網上搶最後一本書

表 book(id, name, price, number),庫存 number=1。用戶甲乙近同時下單。

無事務/錯誤事務後果

-- 甲: SELECT number=1 → UPDATE number=0
-- 乙: SELECT number=1 (甲未提交) → UPDATE number=-1  -- 超賣!

正確方案:事務 + 行鎖 (SELECT ... FOR UPDATE)

START TRANSACTION;
SELECT number FROM book WHERE id=123 FOR UPDATE;  -- 加排他鎖 (X Lock)
-- 業務邏輯判斷 number > 0
UPDATE book SET number = number - 1 WHERE id=123;
COMMIT;
  • FOR UPDATE 在讀取時加 **排他鎖 (X Lock)**,阻塞其他事務的 FOR UPDATE / LOCK IN SHARE MODE / 寫操作。
  • 若甲事務未提交,乙的 SELECT ... FOR UPDATE 會**阻塞等待**,直到甲 COMMIT 釋放鎖,乙再讀到 number=0 放棄下單。
  • 注意: 普通 SELECT (無鎖) 仍可讀到舊值 (MVCC 快照讀),不受保護。

隔離級別與鎖關係 (InnoDB 默認 REPEATABLE READ)

隔離級別髒讀不可重複讀幻讀鎖策略
READ UNCOMMITTED✅✅✅無鎖
READ COMMITTED❌✅✅行鎖 (RC)
REPEATABLE READ❌❌❌ (Next-Key Lock)行鎖 + 間隙鎖
SERIALIZABLE❌❌❌表鎖/範圍鎖

MySQL 8.0 可通過 SET SESSION TRANSACTION ISOLATION LEVEL ... 動態調整。

PHP 事務實戰代碼 (mysqli 面向對象)

<?php
$mysqli = new mysqli("localhost", "root", "123456", "shop", 3306);
if ($mysqli->connect_errno) die("連接失敗: " . $mysqli->connect_error);

$mysqli->begin_transaction();  // 或 $mysqli->query("START TRANSACTION");

try {
    $sql1 = "INSERT INTO main (num) VALUES (123)";
    $sql2 = "INSERT INTO main (num1) VALUES (456)";  // 故意寫錯欄位名模擬失敗
    
    if (!$mysqli->query($sql1)) throw new Exception("SQL1 失敗: " . $mysqli->error);
    if (!$mysqli->query($sql2)) throw new Exception("SQL2 失敗: " . $mysqli->error);
    
    $mysqli->commit();
    echo "提交成功";
} catch (Exception $e) {
    $mysqli->rollback();
    echo "回滾: " . $e->getMessage();
}
$mysqli->close();
?>

避坑指南

  • ⚠️ **表引擎必須 InnoDB**:SHOW CREATE TABLE t 確認 ENGINE=InnoDB。
  • ⚠️ **自動提交默認開啟**:SET autocommit=0 或顯式 BEGIN 開啟事務。
  • ⚠️ **DDL 隱式提交**:CREATE/DROP/ALTER TABLE 會隱式提交當前事務,勿混用。
  • ⚠️ **長事務危害**:持有鎖時間長 → 阻塞併發、Undo Log 膨脹、主從延遲。拆分為小事務或用樂觀鎖。

結語: 事務是資料一致性的基石。**InnoDB + 顯式 BEGIN/COMMIT/ROLLBACK + SELECT FOR UPDATE 行鎖** 是處理併發寫入的標準組合拳。

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