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

MySQL 表鎖機制深度解析與併發控制實戰

     編輯:MySQL 綜合教學

MySQL 表鎖(LOCK TABLES)是處理高併發場景下數據一致性的重要手段。本文深入解析鎖模式、行為規則,並通過「庫存扣減」經典案例演示如何用表鎖解決併發超賣問題。

表鎖核心概念

LOCK TABLES 創建一個「封閉空間」,鎖定的表進入該空間,直到 UNLOCK TABLES 或會話結束才釋放。空間內僅可訪問已鎖定的表,其他表不可用。

鎖模式對比

模式當前會話其他會話適用場景
READ可讀,不可寫(報錯)可讀,寫請求等待只讀報表、備份
WRITE可讀可寫讀寫請求均等待數據修改、關鍵業務

語法

-- 鎖定多表,指定模式
LOCK TABLES `table1` READ, `table2` WRITE, `table3` READ;

-- 釋放所有鎖
UNLOCK TABLES;

併發超賣問題與解決

問題場景

商品庫存 1 件,用戶 A、B 幾乎同時下單。無鎖保護時:

  1. A 查詢庫存 = 1
  2. B 查詢庫存 = 1(A 尚未更新)
  3. A 扣減庫存 = 0,下單成功
  4. B 扣減庫存 = -1,下單成功 → 超賣

加鎖修正代碼

<?php
// 1. 獲取寫鎖(排他鎖)
$db->lock('goods', 2);  // 2 = WRITE

// 2. 安全檢查庫存並扣減
$sql = "SELECT number FROM goods WHERE id = 1";
$number = intval($db->result($db->query($sql), 0));

if ($number > 0) {
    sleep(2);  // 模擬業務處理延遲
    $sql = "UPDATE goods SET number = number - 1 WHERE id = 1";
    if ($db->query($sql)) {
        echo '下單成功';
    } else {
        echo '系統錯誤,請重試';
    }
} else {
    echo '庫存不足';
}

// 3. 釋放鎖
$db->unlock();
?>

封裝類方法

class MySQL_DB {
    // ... 連接屬性 ...
    
    public function lock($table, $type = 1) {
        $mode = ($type == 1) ? 'READ' : 'WRITE';
        $this->query("LOCK TABLE `$table` $mode");
    }
    
    public function unlock() {
        $this->query("UNLOCK TABLES");
    }
}

壓測驗證

使用 JMeter 模擬 2 個併發線程:

  • 無鎖:兩請求均返回成功,庫存變 -1
  • 加鎖:第一請求成功,第二請求等待鎖釋放後查詢庫存為 0,返回「庫存不足」

JMeter 線程組配置 無鎖測試結果 加鎖測試結果

關鍵注意事項

  1. 自動釋放:會話斷開、執行新 LOCK TABLES 均會自動釋放舊鎖
  2. 死鎖風險:多表加鎖順序不一致易導致死鎖,規範統一加鎖順序
  3. 性能代價:表鎖粒度大,併發度低;InnoDB 行鎖更適合高併發寫入場景
  4. 事務優選:InnoDB 事務(START TRANSACTION + 行鎖)是更現代的併發控制方案

總結

表鎖是 MyISAM 時代解決併發的利器,但在 InnoDB 為主的現代架構中,行級鎖 + 事務 才是首選。理解表鎖機制有助於維護舊系統、排查鎖衝突,也為學習事務隔離級別打下基礎。

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