MySQL 悲觀鎖、樂觀鎖

更新 發佈閱讀 9 分鐘

假設有一張表,儲存一個iPhone商品跟剩餘庫存量。

raw-image

假設A/B兩個人同時間進來買商品,如果沒有任何防護機制,資料庫中的商品剩餘庫存量有可能會記錄錯誤,比如iPhone庫存剩10個,此時兩個人"同時"進來搶,由於是同時,所以select到的in memory庫存量都會是10,A/B都會把庫存量-1(10-1)再update資料表,這種情況下剩餘庫存量會是9而不是8,就會發生記錄錯誤的問題。

如果商品庫存剩1個,明明是A先買到,這時候就已經沒庫存了,可是系統卻告訴B恭喜你搶到了,這種情況就更糟糕了,只剩下一支iPhone,卻跟兩個人(甚至更多人)都說有買到,也就是發生所謂的超賣問題。

所以資料庫必須有一些機制來保證資料的正確性,來避免上述所說的問題。


悲觀鎖 Pessimistic Lock

  • 使用資料庫 Transaction 機制來強迫執行順序,確保每次都只有一個事務執行。

每次下SQL時都確保同時只會有一個事務執行,也就是等A SELECT出庫存量並且-1 UPDATE庫存量之後,才釋放出Lock換B執行,這時候B SELECT拿到的庫存數量就會是9,9-1=8再去UPDATE庫存量,也就可以解決數據不正確的問題。

模擬悲觀鎖:

mysql> begin;
mysql> select stock from `product` where id = 1 for update;
mysql> update `product` set stock = stock -1 where id = 1;
mysql> commit;

可開啟兩個mysql console視窗模擬,A視窗下了for update 會馬上跑出SELECT結果,這時候B視窗也下了這行,會發現沒有馬上執行,必須等待A視窗commit後釋放出lock,B視窗才會繼續執行,等到commit後才又把lock釋放出來。

  • 注意:上述需要支援 Transaction,本文是用InnoDB儲存引擎,MyISAM 是不支援 Transaction 的,如果是用MyISAM會發現for update根本就不會lock住。
  • 悲觀鎖適合寫多讀少的情境。
  • 這種方法的缺點是,如果同時出現大量的讀取操作,由於Lock機制,會需要等待時間,因此可能會有效能問題。

樂觀鎖 Optimistic Lock

  • 因為樂觀,所以DB不會上鎖,但是在update的時候會檢查,在這期間有沒有人更新過資料,一般會加一個version或timestamp欄位來判斷,這種作法並不是利用DB的Transaction,而是一種人為上鎖的機制。

模擬樂觀鎖: 新增一個version欄位

raw-image
mysql> SELECT stock, version FROM `product` where id = 1;
mysql> update `product` set stock = stock-1, version = version + 1 where id = 1 and version = 0;
raw-image

兩個視窗同時都會拿到stock=10, version=0的資料,A視窗update資料的時候卡上version=0條件,會更新成功。但這時候B視窗一樣會卡上version=0的條件來更新資料,會發現根本找不到version=0的資料,因為早就被A改成version=1了,也就更新失敗了,這時候應該要通知B使用者購買失敗。

試想如果沒有多version這個欄位當作where條件去更新的話,這筆row data兩個視窗都會更新成功,也就是沒有判斷這段期間有沒有別人更新過,如果庫存剩一個的話,就會發生賣超的問題,剩餘庫存量還記錄為-1。

  • 樂觀鎖適合讀多寫少的情境。
  • 這種方法不適合用於寫多讀少的情況下,因為同時很多併發發生的時候,會有很多update失敗需要重試的狀況發生,消耗的資源開銷會比悲觀鎖更多。

Laravel中使用悲觀鎖(上述的模擬悲觀鎖):

raw-image
try {
  DB::beginTransaction();
  $data = Product::where('id', 1)->lockForUpdate()->first();
  if($data->stock <= 0){ //庫存不足
    return "庫存不足";
  }

  $data->decrement('stock', 1);
  DB::commit();
}
catch (Exception $e) {
  DB::rollback();
}

transaction簡潔寫法:

raw-image
$result = DB::transaction(function () {
  $data = Product::where('id', 1)->lockForUpdate()->first();
  if($data->stock <= 0){ //庫存不足
    return false;
  }

  $data->decrement('stock', 1);
  return true;
});
if(!$result){
  return '庫存不足';
}


  • ->lockForUpdate()就是SQL中FOR UPDATE的意思。
  • ->lockForUpdate()要搭配transaction語法一起使用,不然沒有效果。

LOCK IN SHARE MODE

這種鎖法對應到Laravel的->sharedLock()。

比較一下跟FOR UPDATE鎖法差別:

  • LOCK IN SHARE MODE: 不會阻止其它transaction讀取同一行。
  • FOR UPDATE: 會阻止其它transaction讀取同一行(一般沒加鎖的select還是可以讀取,不影響)。
  • 共同之處是兩者都會阻止同一行資料被其它transaction update。

所以如果是超賣的情境,不適合用LOCK IN SHARE MODE,因此使用哪一種lock方法,還是要視系統情境而定,不能亂用。



本筆記參考:

  1. https://www.codeprj.com/zh/blog/bb96e01.html
  2. https://www.gushiciku.cn/pl/gKzx/zh-tw
  3. https://ithelp.ithome.com.tw/articles/10271229
  4. https://www.readfog.com/a/1634624578764509184
  5. https://medium.com/dean-lin/%E7%9C%9F%E6%AD%A3%E7%90%86%E8%A7%A3%E8%B3%87%E6%96%99%E5%BA%AB%E7%9A%84%E6%82%B2%E8%A7%80%E9%8E%96-vs-%E6%A8%82%E8%A7%80%E9%8E%96-2cabb858726d
  6. https://www.w3help.cc/a/202108/569509.html
  7. https://twgreatdaily.com/uTc0AHEBnkjnB-0zi2R_.html
  8. https://blog.51cto.com/u_9443450/2394262
  9. https://ithelp.ithome.com.tw/articles/10267450
  10. https://kknews.cc/zh-tw/news/z9aqnva.html
  11. https://blog.csdn.net/weixin_39947314/article/details/113720708
  12. https://learnku.com/articles/36846
  13. http://www.yangxg.com/blog/id/1530105560
  14. https://learnku.com/articles/57959
  15. https://www.sunzhongwei.com/using-laravel-sharedlock-and-lockforupdate-for-table-row-locks


留言
avatar-img
留言分享你的想法!
avatar-img
Vic Lin的沙龍
20會員
161內容數
Vic Lin的沙龍的其他內容
2023/08/13
父元件 傳遞方法使用@ <template>    ...    <Login @modalClose="modalClose"/> ... </template> <script setup>     const _modal = ref();     function m
2023/08/13
父元件 傳遞方法使用@ <template>    ...    <Login @modalClose="modalClose"/> ... </template> <script setup>     const _modal = ref();     function m
2023/03/25
前情提要 由於我的筆電已經用了10年,無法再戰下去了,且有預算考量,加上使用電腦幾乎都是定點,只有偶爾回家的時候會需要攜帶,因此最終選擇了迷你電腦,體積小不占空間,又方便攜帶,剛好符合我的需求。 菜單 由於這台無法裝獨顯,所以CPU的部分選擇 AMD R5 3400G(含Vega 11內
Thumbnail
2023/03/25
前情提要 由於我的筆電已經用了10年,無法再戰下去了,且有預算考量,加上使用電腦幾乎都是定點,只有偶爾回家的時候會需要攜帶,因此最終選擇了迷你電腦,體積小不占空間,又方便攜帶,剛好符合我的需求。 菜單 由於這台無法裝獨顯,所以CPU的部分選擇 AMD R5 3400G(含Vega 11內
Thumbnail
2023/03/10
Nuxt3中可使用useFetch來獲取數據,不須再引用axios,相當方便: 本筆記參考: https://juejin.cn/post/7104071421160063012 https://juejin.cn/post/7086472647575339045
2023/03/10
Nuxt3中可使用useFetch來獲取數據,不須再引用axios,相當方便: 本筆記參考: https://juejin.cn/post/7104071421160063012 https://juejin.cn/post/7086472647575339045
看更多
你可能也想看
Thumbnail
※ 為什麼我們需要 Transaction? 當我們談到 Transaction(交易)時,指的是一組不可分割的 SQL 操作。這些操作結果只能成功或失敗,以確保資料庫的一致性和完整性。Transaction 是資料庫操作中的一個「邏輯單位」,包含多個操作步驟。如果其中任何一個步驟失敗,整個 Tr
Thumbnail
※ 為什麼我們需要 Transaction? 當我們談到 Transaction(交易)時,指的是一組不可分割的 SQL 操作。這些操作結果只能成功或失敗,以確保資料庫的一致性和完整性。Transaction 是資料庫操作中的一個「邏輯單位」,包含多個操作步驟。如果其中任何一個步驟失敗,整個 Tr
Thumbnail
產品在導入期通常銷售狀況不穩定。 為了滿足消費者隨時可以取得產品非常重要, 一旦出現缺貨會大大影響銷售及市場布局。 消費者常常衝動消費當下沒有達成交易 , 很可能購買替代品或失去消費動機。 也就是說在需要大量供貨或小量補單的時候都能符合需求提供給企業, 這樣才是最理想的狀態。 但庫存和生產一直是一個
Thumbnail
產品在導入期通常銷售狀況不穩定。 為了滿足消費者隨時可以取得產品非常重要, 一旦出現缺貨會大大影響銷售及市場布局。 消費者常常衝動消費當下沒有達成交易 , 很可能購買替代品或失去消費動機。 也就是說在需要大量供貨或小量補單的時候都能符合需求提供給企業, 這樣才是最理想的狀態。 但庫存和生產一直是一個
Thumbnail
眾生畏因,菩薩畏果,佛畏系統 交易也是 散戶怕大崩盤,分析師怕大崩盤的原因, 而高手則在意自己的交易系統能否應付這風險 交易系統要知道自己的優勢劣勢在哪裡而且附合乎自己個性 這裡分享我自己交易系統 優勢:下檔有限,上檔靠運氣 劣勢:只有賺很多才allin 有幾個sop 1.多空判斷 2.小量試單(標
Thumbnail
眾生畏因,菩薩畏果,佛畏系統 交易也是 散戶怕大崩盤,分析師怕大崩盤的原因, 而高手則在意自己的交易系統能否應付這風險 交易系統要知道自己的優勢劣勢在哪裡而且附合乎自己個性 這裡分享我自己交易系統 優勢:下檔有限,上檔靠運氣 劣勢:只有賺很多才allin 有幾個sop 1.多空判斷 2.小量試單(標
Thumbnail
設想一個情境,你在盤前規劃好今天要做多A股票(前一天的收盤價在102元),等它回檔來到100元的時候你就要買進,停損點設定在95元,停利點則設定在漲停板112元。 就在你猶豫的時候,行情又再度下跌,最低來到96元,這個時候十個大概有八個都會暗自竊喜,好險剛剛沒有買,不然又要虧錢了。
Thumbnail
設想一個情境,你在盤前規劃好今天要做多A股票(前一天的收盤價在102元),等它回檔來到100元的時候你就要買進,停損點設定在95元,停利點則設定在漲停板112元。 就在你猶豫的時候,行情又再度下跌,最低來到96元,這個時候十個大概有八個都會暗自竊喜,好險剛剛沒有買,不然又要虧錢了。
Thumbnail
假設有一張表,儲存一個iPhone商品跟剩餘庫存量。 如果商品庫存剩1個,明明是A先買到,這時候就已經沒庫存了,可是系統卻告訴B恭喜你搶到了,這種情況就更糟糕了,只剩下一支iPhone,卻跟兩個人(甚至更多人)都說有買到,也就是發生所謂的超賣問題。 悲觀鎖 Pessimistic Lock
Thumbnail
假設有一張表,儲存一個iPhone商品跟剩餘庫存量。 如果商品庫存剩1個,明明是A先買到,這時候就已經沒庫存了,可是系統卻告訴B恭喜你搶到了,這種情況就更糟糕了,只剩下一支iPhone,卻跟兩個人(甚至更多人)都說有買到,也就是發生所謂的超賣問題。 悲觀鎖 Pessimistic Lock
Thumbnail
我修改了網格交易管理表的【買賣速度】參數。這篇文章會說明原因以及修改此參數的操作方法。
Thumbnail
我修改了網格交易管理表的【買賣速度】參數。這篇文章會說明原因以及修改此參數的操作方法。
Thumbnail
2021/07/12 【今日檢討與想法】 有很深刻的感覺,在交易的手法上有些微的矛盾 有兩種不同的方式,個別做問題不大 但當一天之中要使用這兩個方式,就會產生額外的遲疑跟矛盾 還無法依照行情使用較為適合的手法,還融合與駕馭。 從明天開始應該要保守交易以防萬一
Thumbnail
2021/07/12 【今日檢討與想法】 有很深刻的感覺,在交易的手法上有些微的矛盾 有兩種不同的方式,個別做問題不大 但當一天之中要使用這兩個方式,就會產生額外的遲疑跟矛盾 還無法依照行情使用較為適合的手法,還融合與駕馭。 從明天開始應該要保守交易以防萬一
Thumbnail
2021/05/31 人們總是把剛剛經歷過的美好或恐懼無限放大,導致過度美好或過度恐懼。 這也大概是為什麼股票市場,不是超漲就是超跌。 以前高中很愛打三國跟信長,我就問一個朋友你整天只玩電腦不出門ㄝ 他跟我說:電腦是絕對不會背叛你的
Thumbnail
2021/05/31 人們總是把剛剛經歷過的美好或恐懼無限放大,導致過度美好或過度恐懼。 這也大概是為什麼股票市場,不是超漲就是超跌。 以前高中很愛打三國跟信長,我就問一個朋友你整天只玩電腦不出門ㄝ 他跟我說:電腦是絕對不會背叛你的
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News