1. 程式人生 > 程式設計 >樂觀鎖與悲觀鎖

樂觀鎖與悲觀鎖

排他鎖

  • 排它鎖,加鎖後別的使用者可以讀取資料,但是不能更改資料
  • mysql中, for update 是一種使用排它鎖的方式
  • 通過主鍵選中的。那麼這個時候是行級鎖
  • 選中的條件不明確包含主鍵。這個時候會鎖表

悲觀鎖

悲觀鎖的簡介

  • 悲觀鎖:當我們要對一個資料庫中的一條資料進行修改的時候,為了避免同時被其他人修改,最好的辦法就是直接對該資料進行加鎖以防止併發。
  • 悲觀併發控制實際上是“先取鎖再訪問”的保守策略,為資料處理的安全提供了保證。
  • img
  • 但是在效率方面,處理加鎖的機制會讓資料庫產生額外的開銷,還有增加產生死鎖的機會,同時也會降低併發效能

悲觀鎖的實現

  • 注意: 要使用悲觀鎖,我們必須關閉mysql資料庫的自動提交屬性。MySQL預設使用autocommit模式,也就是說,當你執行一個更新操作後,MySQL會立刻將結果進行提交。我們可以使用命令設定MySQL為非autocommit模式:set autocommit=0;

  • 舉一個栗子

    //開始事務
    begin; 
    //查詢出商品庫存資訊,使用 for update 加上排它鎖
    select quantity from items where id=1 for update;
    //修改商品庫存
    update items set quantity=100 where id = 1;
    //提交事務
    commit;
    複製程式碼

樂觀鎖

樂觀鎖的簡介

  • 樂觀鎖在資料進行提交更新的時候,才會正式對資料的衝突與否進行檢測
  • 相對於悲觀鎖,在對資料庫進行處理的時候,樂觀鎖並不會使用資料庫提供的鎖機制。一般的實現樂觀鎖的方式就是記錄資料版本。
  • img
  • 樂觀鎖不會產生任何鎖和死鎖,擁有更好的效能。

樂觀鎖的實現

  1. 通過一個單獨的可以順序遞增的version欄位,可以避免ABA問題:

    //查詢出商品資訊,version = 1
    select version from items where id=1
    //修改商品庫存為2
    update items set quantity=2,version = 3 where id=1 and version = 2;
    複製程式碼

    除了version以外,還可以使用時間戳,因為時間戳天然具有順序遞增性。

  2. 減小樂觀鎖力度,可以最大程度的提升吞吐率,提高併發能力:

    //修改商品庫存
    update item 
    set quantity=quantity - 1 
    where id = 1 and quantity - 1 > 0
    複製程式碼

悲觀鎖和樂觀鎖,該選擇哪個?

  • 樂觀鎖並未真正加鎖,效率高。然而,如果鎖的粒度掌握不好,更新失敗的概率就會比較高,容易發生業務失敗。
  • 悲觀鎖依賴資料庫鎖,效率低。更新失敗的概率比較低。
  • 在高併發的業務場景下,悲觀鎖越來越少被使用了。

參考