动态

详情 返回 返回

thinkphp中行鎖(Lock)操作跟事務的關係以及用法 - 动态 详情

事務跟行鎖(Lock)的關係

首先解釋下事務和鎖各自的作用

  • 事務的作用
    事務主要保證一組數據庫操作(增刪改)的原子性, 即要麼全部執行成功,要麼全部失敗,避免出現數據不一致的中間狀態.
  • 加鎖的作用
    加鎖主要解決併發場景下的數據競爭問題, 比如多個請求同時修改同一條數據時,可能導致"髒讀""不可重複讀"等問題.

    如果僅僅使用事務不用鎖的侷限性

    事務操作雖然能保證原子性,但是但是無法阻止併發場景下的數據競爭問題. 以'庫存扣減'為例,假設商品初始庫存為10,同時有兩個訂單(A和B)都要買5件,流程都是"查詢庫存->判斷足夠->扣減庫存".

    場景1:僅用事務不加鎖會出現超賣
  • 訂單 A 的事務開始,查詢庫存:10(此時還沒扣減)。
  • 訂單 B 的事務開始,也查詢庫存:10(因為 A 的扣減還沒提交,B 讀到的是舊數據)。
  • 訂單 A 判斷庫存足夠,扣減 5,庫存變為 5,提交事務。
  • 訂單 B 也判斷庫存足夠(基於之前讀到的 10),扣減 5,庫存變為 0,提交事務。
    結果: 最終庫存0,兩個訂單各賣5件,看似沒有問題,但如果兩個訂單都買6件哪?
  • A查庫存10->扣6(剩4)->提交
  • B查庫存10->扣6(剩-2)->提交
    這裏事務雖然保證了A和B各自的'扣減操作'是原子性的(不會只扣一半), 但無法阻止B在A未完成時讀取並修改同一份庫存數據,這就是併發數據競爭導致的問題.

    場景2: 事務+鎖,避免超賣

    加鎖的作用是讓併發操作"排隊",確保同一時間只有一個事務能夠修改目標數據,其它事務必須等待,從而避免基於舊數據做判斷.

  • 悲觀鎖(直接鎖定數據,阻止併發修改)
  • 在查詢庫存的時候就鎖定對應數據, 其它事務必須等當前事務完成才能操作

    事務跟行鎖(Lock)結合使用

    在 ThinkPHP 中,使用行鎖(Lock) 鎖定記錄時,必須將鎖操作寫在事務內部。這是因為數據庫的行鎖依賴事務的上下文,只有在事務中才能保證鎖的有效性和原子性。

    原因分析

  • 行鎖的特性:
    數據庫的行鎖(如for update)需要在事務中生效, 事務提交或者回滾後,鎖會自動釋放. 如果在事務外執行行鎖操作,鎖會立即釋放,無法達到預期的鎖定效果.
  • 原子性保證
    事務的核心作用是保證一組操作的原子性(要麼全部成功,要麼全部失敗).將鎖操作放在事務內,可以確保鎖定的記錄在事務完成前不會被其它事務修改,避免併發衝突.

    正確示例(鎖在事務內部)

    use think\Db;
    use app\model\User;
    
    // 開啓事務
    Db::startTrans();
    try {
      // 1. 在事務內鎖定記錄(FOR UPDATE)
      $user = User::where('id', 1)
          ->lock(true) // 等同於 FOR UPDATE
          ->find();
      
      if (!$user) {
          throw new \Exception('用户不存在');
      }
      
      // 2. 執行更新操作(依賴鎖定的記錄)
      $user->balance -= 100;
      $user->save();
      
      // 3. 提交事務(鎖自動釋放)
      Db::commit();
      echo '操作成功';
    } catch (\Exception $e) {
      // 回滾事務(鎖自動釋放)
      Db::rollback();
      echo '操作失敗:' . $e->getMessage();
    }

    錯誤的示例(鎖在事務外)

    // 錯誤:鎖在事務外,會立即釋放
    $user = User::where('id', 1)->lock(true)->find();
    
    Db::startTrans();
    try {
      // 此時鎖已釋放,可能被其他事務修改
      $user->balance -= 100;
      $user->save();
      Db::commit();
    } catch (\Exception $e) {
      Db::rollback();
    }
  • 上面這種寫法lock(true)在事務外執行,查詢結束後鎖會立即釋放,無法阻止其它事務修改記錄,可能導致數據不一致

    總結

    必須將鎖操作(lock(true))寫在事務內部,才能保證鎖的有效性和併發安全。正確流程是:
    開啓事務 → 鎖定記錄 → 執行操作 → 提交/回滾事務。
    這種方式可以有效避免併發場景下的資源競爭,確保數據一致性。

user avatar ciel717 头像 liu_486 头像 bao_686ce718ec240 头像 kanjianliao 头像 shuirongshui 头像 _61e9689d548cc 头像
点赞 6 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.