首頁  >  文章  >  資料庫  >  Redis常見分佈鎖的原理與實作(總結分享)

Redis常見分佈鎖的原理與實作(總結分享)

WBOY
WBOY轉載
2022-08-25 11:53:502429瀏覽

推薦學習:Redis影片教學

#Java中的鎖主要包括synchronized鎖定和JUC套件中的鎖,這些鎖都是針對單一JVM實例上的鎖,對於分散式環境是無效的,那麼基於分散式鎖的如何實現呢?

常見的分散式鎖定的實作如下圖:

#基於資料庫

##悲觀鎖定

悲觀鎖(Pessimistic Lock)顧名思義為很悲觀的鎖,每次在拿資料的時候都會上鎖。這樣別人想拿資料就被擋住,直到悲觀鎖被釋放,悲觀鎖中的共享資源每次只給一個線程使用,其它線程阻塞,用完後再把資源轉讓給其它線程,但是在效率方面,處理加鎖的機制會產生額外的開銷,且容易產生死鎖。

實現原理

悲觀並發控制實際上是"先取鎖再存取"的保守策略,為資料處理的安全提供了保證.

具體實作

例如透過悲觀鎖定來實現庫存扣減的偽代碼如下:

// 对于库存记录进行行锁

SELECT *FROM sys_goods s WHERE s.Id='1' FOR UPDATE;

//执行库存扣减
update sys_stock s set s.stockQty=s.stockQty-#{number} where s.goodId=1 and s.stockQty>0;

//提交事务,自动释放悲观锁。

樂觀鎖定

簡介

樂觀鎖是基於資料版本號(version)的機制來實現的。資料庫表新增"version"欄位, 讀取出資料時,將此版本號讀出,在更新過程中,會對版本號進行比較,如果是一致的,則會成功執行本次操作,且版本號加1,如果版本號不一致,則會更新失敗。

實作原理

相對悲觀鎖,樂觀鎖的實作不會使用到資料庫的鎖機制,樂觀鎖的原理使用的CAS的機制來實現的,CAS(Compare-and- Swap)即比較並替換.

    1、
  • 比較:讀取到了一個值A,在將其更新為B之前,檢查原值是否仍為A(未被其他執行緒改動).
  • 2、
  • 設定:如果是未傳送變化,則將A更新為B結束。如果發生變化,則什麼都不做。
具體實作

例如樂觀鎖定來實作庫存扣減的偽代碼如下:

// 查询库存记录,获取版本号
SELECT stockQty,version FROM sys_goods s WHERE s.Id='1'

//执行库存扣减,防止出现超卖
update sys_stock s set 
  s.stockQty=s.stockQty-#{number},
  s.version=version+1
  where s.goodId=1 and s.stockQty>0 and version=#{version};

Redis實作分散式鎖定

關於Redis分散式鎖定的實現,已經在前期的文章中進行了講解,大家可以參考如下文章

Spring Boot 實現Redis分散式鎖定原理

Spring Boot 集成Redisson實作分散式鎖定詳細案例

Zooker實作分散式鎖定

Zookper實作分散式鎖定,主要是應用zookeeper節點的暫存和有序性來實作。

加上鎖定過程

當客戶端1請求時,Zookeeper客戶端會建立一個持久節點Locks節點,如果客戶端1想取得鎖定,會在locks節點下建立臨時節點/ node_000000,如果尋找Locks下面所有臨時有序子節點,當自己為最小的節點是則取得鎖成功。

當客戶端2嘗試取得鎖定時,也會查看locks下面的暫存節點,判斷自己的節點/node_000001是不是最小,如果不是最小則取得鎖定失敗,客戶端2會向它排序靠前的節點node_000000註冊watch事件,用來監聽node_000000是否存在,雖然搶鎖失敗,但是node_000001進入等待狀態。

釋放鎖定的過程

Zookeeper的用戶端業務完成或用戶端發生故障,都會刪除暫存節點並且釋放鎖定。如果是任務完成,客戶端1也會明確呼叫刪除node_000000的指令。

例如上述圖,客戶端1斷開,臨時節點node_000000已被刪除,而此時node_000001透過watcher監聽發現自己為為最小的臨時節點,所以獲取鎖成功。

异常场景分析

客户端1创建临时节点后,会与Zookeeper服务器维护一个Session,这个Session会依赖客户端 定时心跳来维持连接。由于网路异常原因,Zookeeper长时间收不到客户端1的心跳,就认为这个Session过期了,也会把这个临时节点删除,此时客户端2创建临时节点能够获取锁成功。当客户端网络恢复正常后,它仍然认为持有锁,此时就会造成锁冲突。

具体实现

Zookeeper实现分布式锁,可以采用Curator实现分布式锁,关于SpringBoot如何集成Curator,大家可以参考如下文章:

Java Spring Boot 集成Zookeeper

Zookpeer实现分布式锁实现库存扣减

 @RequestMapping("/lockStock")
    public void lockStock()
    {
       zooKeeperUtil.lock("/Locks", 1000, TimeUnit.SECONDS, ()->{
           //业务逻辑
       });
    }

小结:

关于分布式锁的实现的对比,详情请查看下图:

推荐学习:Redis视频教程

以上是Redis常見分佈鎖的原理與實作(總結分享)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:jb51.net。如有侵權,請聯絡admin@php.cn刪除