樂觀鎖
開啓事務前,設置對數據的監聽(watch),EXEC時,如果發生數據發生過修改,作用於改數據的事務會自動取消(DISCARD),事務EXEC後,無論成敗,監聽會被移除
悲觀鎖
每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖。
場景:如果項目中使用了緩存且對緩存設置了超時時間。
當併發量比較大的時候,如果沒有鎖機制,如果有大量請求訪問過期的數據,那麼大量併發請求會穿透緩存直接查詢數據庫,造成雪崩效應。
用加鎖或者隊列的方式保證來保證不會有大量的線程對數據庫一次性進行讀寫,從而避免失效時大量的併發請求落到底層存儲系統上,加鎖排隊只是爲了減輕數據庫的壓力,並沒有提高系統吞吐量。假設在高併發下,緩存重建期間key是鎖着的,這是過來1000個請求999個都在阻塞的。同樣會導致用戶等待超時,這是個治標不治本的方法!
應用場景一:
多個客戶端可能同時操作同一組數據,並且該數據一旦被操作修改後,便不適合在原基礎上繼續操作,如四個業務員對一個物品進行入庫操作,一名業務員操作完成之後,其他的業務員將不可以再原先基礎的數量上進行入庫操作
解決方案:
加鎖(樂觀鎖)
# 對key添加監視鎖,在執行exec前如果key發生了變化,終止事務執行
watch key1 [key2...]
#取消對所有key的監視
unwatch
應用基於狀態控制的批量任務執行,此應用場景有弊端,如果是很多用戶購買同一件商品,如果用它來監控數據庫的數量的,每次購買都將用戶的訂單消除掉?這是不現實的,有100個用戶同時購買一件物品,一個用戶購買成功之後,就將99人的訂單消除掉,再讓99人重新下訂單,以此類推。
應用場景二
超賣:
如何避免最後一件商品不被多人同時購買?避免用戶在沒有庫存的時候不能下單
業務分析:
- 使用watch監控一個key有沒有改變已經不能解決問題,此處要監控的時具體的數據
- 雖然redis是單線程的,但是多個客戶端對同一數據及逆行操作時,如果避免不被同時修改
解決方案:
分佈式鎖(悲觀鎖)
#使用setnx設置一個公共鎖,這個lock-key必須相同,如果都不規範,那就亂套了,vlue隨便設置
setnx lock-key valule
大量用戶要購買一件商品,首先要做的是看看有沒有人用(是否上鎖),如果沒有拿到執行權,進行購物(數據庫數量減1操作),如果有鎖,那就排隊等待
利用setnx命令的返回值特徵,有值則返回設置失敗,無值則返回設置成功
- 對於返回設置成功,擁有控制權,進行下一步的具體業務操作
- 對於返回設置失敗的,不具有控制權,排隊或等待
操作完畢通過del操作釋放鎖
應用於基於分佈式鎖對應的場景控制
應用場景三
問題描述:
符合上面以上兩種情形,用戶獲得鎖之後,宕機了,如何解決?
業務分析:
- 由於鎖操作由用戶控制加鎖解鎖,必定會存在加鎖未解鎖的風險
- 需要解鎖操作不能僅依賴用戶控制,系統級別也要給出保底方案:定時解鎖
解決方案:
#使用expire爲key添加事件限定,到時不釋放,放棄鎖
expire lock-key second
pexpire lock-key milliseconds
由於操作通常都是微妙或毫秒級,因此改鎖定事件不宜設置過大,具體事件需要業務測試後確認
- 持有鎖的操作最長執行事件127ms,最短執行事件7ms
- 鎖事件設定推薦:最大耗時120%+平均網絡延遲110%