自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

分布式鎖的各種實(shí)現(xiàn),看完這篇你就懂了!

云計(jì)算 分布式
Redis提供了高效的獲取鎖和釋放鎖的操作,而且結(jié)合Lua腳本,Redission等,有比較好的異常情況處理方式,因?yàn)槭腔趦?nèi)存的,讀寫效率也是非常高。

前言

今天我們講講分布式鎖,網(wǎng)上相關(guān)的內(nèi)容有很多,但是比較分散,剛好自己剛學(xué)習(xí)完總結(jié)下,分享給大家,文章內(nèi)容會比較多,我們先從思維導(dǎo)圖中了解要講的內(nèi)容。

圖片圖片

什么是分布式鎖

分布式鎖是控制分布式系統(tǒng)之間同步訪問共享資源的一種方式,通過互斥來保持一致性。

了解分布式鎖之前先了解下線程鎖和進(jìn)程鎖:

線程鎖:主要用來給方法、代碼塊加鎖。當(dāng)某個方法或代碼使用鎖,在同一時刻僅有一個線程執(zhí)行該方法或該代碼段。線程鎖只在同一JVM中有效果,因?yàn)榫€程鎖的實(shí)現(xiàn)在根本上是依靠線程之間共享內(nèi)存實(shí)現(xiàn)的,比如Synchronized、Lock等

進(jìn)程鎖:控制同一操作系統(tǒng)中多個進(jìn)程訪問某個共享資源,因?yàn)檫M(jìn)程具有獨(dú)立性,各個進(jìn)程無法訪問其他進(jìn)程的資源,因此無法通過synchronized等線程鎖實(shí)現(xiàn)進(jìn)程鎖

比如Golang語言中的sync包就提供了基本的同步基元,如互斥鎖

但是以上兩種適合在單體架構(gòu)應(yīng)用,但是分布式系統(tǒng)中多個服務(wù)節(jié)點(diǎn),多個進(jìn)程分散部署在不同節(jié)點(diǎn)機(jī)器中,此時對于資源的競爭,上訴兩種對節(jié)點(diǎn)本地資源的鎖就無效了。

這個時候就需要分布式鎖來對分布式系統(tǒng)多進(jìn)程訪問資源進(jìn)行控制,因此分布式鎖是為了解決分布式互斥問題!

圖片圖片

分布式鎖的特性

互斥

互斥性很好理解,這也是最基本功能,就是在任意時刻,只能有一個客戶端才能獲取鎖,不能同時有兩個客戶端獲取到鎖。

避免死鎖

為什么會出現(xiàn)死鎖,因?yàn)楂@取鎖的客戶端因?yàn)槟承┰?如down機(jī)等)而未能釋放鎖,其它客戶端再也無法獲取到該鎖,從而導(dǎo)致整個流程無法繼續(xù)進(jìn)行。

圖片圖片

面對這種情況,當(dāng)然有解決辦法啦!

引入過期時間:通常情況下我們會設(shè)置一個 TTL(Time To Live,存活時間) 來避免死鎖,但是這并不能完全避免。

1. 比如TTL為5秒,進(jìn)程A獲得鎖

2. 問題是5秒內(nèi)進(jìn)程A并未釋放鎖,被系統(tǒng)自動釋放,進(jìn)程B獲得鎖

3. 剛好第6秒時進(jìn)程A執(zhí)行完,又會釋放鎖,也就是進(jìn)程A釋放了進(jìn)程B的鎖

僅僅加個過期時間會設(shè)計(jì)到兩個問題:鎖過期和釋放別人的鎖問題

鎖附加唯一性:針對釋放別人鎖這種問題,我們可以給每個客戶端進(jìn)程設(shè)置【唯一ID】,這樣我們就可以在應(yīng)用層就進(jìn)行檢查唯一ID。

自動續(xù)期:鎖過期問題的出現(xiàn),是我們對持有鎖的時間不好進(jìn)行預(yù)估,設(shè)置較短的話會有【提前過期】風(fēng)險(xiǎn),但是過期時間設(shè)置過長,可能鎖長時間得不到釋放。

這種情況同樣有處理方式,可以開啟一個守護(hù)進(jìn)程(watch dog),檢測失效時間進(jìn)行續(xù)租,比如Java技術(shù)??梢杂肦edisson來處理。

可重入:

一個線程獲取了鎖,但是在執(zhí)行時,又再次嘗試獲取鎖會發(fā)生什么情況?

是的,導(dǎo)致了重復(fù)獲取鎖,占用了鎖資源,造成了死鎖問題。

我們了解下什么是【可重入】:指的是同一個線程在持有鎖的情況下,可以多次獲取該鎖而不會造成死鎖,也就是一個線程可以在獲取鎖之后再次獲取同一個鎖,而不需要等待鎖釋放。

解決方式:比如實(shí)現(xiàn)Redis分布式鎖的可重入,在實(shí)現(xiàn)時,需要借助Redis的Lua腳本語言,并使用引用計(jì)數(shù)器技術(shù),保證同一線程可重入鎖的正確性。

容錯

容錯性是為了當(dāng)部分節(jié)點(diǎn)(redis節(jié)點(diǎn)等)宕機(jī)時,客戶端仍然能夠獲取鎖和釋放鎖,一般來說會有以下兩種處理方式:

一種像etcd/zookeeper這種作為鎖服務(wù)能夠自動進(jìn)行故障切換,因?yàn)樗旧砭褪莻€集群,另一種可以提供多個獨(dú)立的鎖服務(wù),客戶端向多個獨(dú)立鎖服務(wù)進(jìn)行請求,某個鎖服務(wù)故障時,也可以從其他服務(wù)獲取到鎖信息,但是這種缺點(diǎn)很明顯,客戶端需要去請求多個鎖服務(wù)。

分類

本文會講述四種關(guān)于分布式鎖的實(shí)現(xiàn),按實(shí)現(xiàn)方式來看,可以分為兩種:自旋、watch監(jiān)聽

自旋方式

基于數(shù)據(jù)庫和基于Redis的實(shí)現(xiàn)就是需要在客戶端未獲得鎖時,進(jìn)入一個循環(huán),不斷的嘗試請求是否能獲得鎖,直到成功或者超時過期為止。

監(jiān)聽方式

這種方式只需要客戶端Watch監(jiān)聽某個key就可以了,鎖可用的時候會通知客戶端,客戶端不需要反復(fù)請求,基于zooKeeper和基于Etcd實(shí)現(xiàn)分布式鎖就是用這種方式。

實(shí)現(xiàn)方式

分布式鎖的實(shí)現(xiàn)方式有數(shù)據(jù)庫、基于Redis緩存、ZooKeeper、Etcd等,文章主要從這幾種實(shí)現(xiàn)方式并結(jié)合問題的方式展開敘述!

基于MySQL

利用數(shù)據(jù)庫表來實(shí)現(xiàn)實(shí)現(xiàn)分布式鎖,是不是感覺有點(diǎn)疑惑,是的,我再寫之前收集資料的時候也有點(diǎn)疑問,雖然這種方式我們并不推崇,但是我們也可以作為一個方案來進(jìn)行了解,我們看看到底怎么做的:

比如在數(shù)據(jù)庫中創(chuàng)建一個表,表中包含方法名等字段,并在方法名name字段上創(chuàng)建唯一索引,想要執(zhí)行某個方法,就使用這個方法名向表中插入一條記錄,成功插入則獲取鎖,刪除對應(yīng)的行就是鎖釋放。

//鎖記錄表
CREATE TABLE `lock_info` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `name` varchar(64) NOT NULL COMMENT '方法名',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_name` (`method_name`) 
) ENGINE=InnoD

這里主要是用name字段作為唯一索引來實(shí)現(xiàn),唯一索引保證了該記錄的唯一性,鎖釋放就直接刪掉該條記錄就行了。

缺點(diǎn)也很多:

  1. 1. 數(shù)據(jù)庫是單點(diǎn),非常依賴數(shù)據(jù)庫的可用性
  2. 2. 需要額外自己維護(hù)TTL
  3. 3. 在高并發(fā)常見下數(shù)據(jù)庫讀寫是非常緩慢

這里我們就不用過多的文字了,現(xiàn)實(shí)中我們更多的是用基于內(nèi)存存儲來實(shí)現(xiàn)分布式鎖。

基于Redis

面試官問:你了解分布式鎖嗎?想必絕大部分面試者都會說關(guān)于Redis實(shí)現(xiàn)分布式鎖的方式,OK,進(jìn)入正題【基于Redis分布式鎖】

Redis 的分布式鎖, setnx 命令并設(shè)置過期時間就行嗎?

setnx lkey lvalue expire lockKey 30

正常情況下是可以的,但是這里有個問題,雖然setnx是原子性的,但是setnx + expire就不是了,也就是說setnx和expire是分兩步執(zhí)行的,【加鎖和超時】兩個操作是分開的,如果expire執(zhí)行失敗了,那么鎖同樣得不到釋放。

關(guān)于為什么要加鎖和超時時間的設(shè)定在文章開頭【避免死鎖】有提到,不明白的可以多看看。

Redis正確的加鎖命令是什么?

//保證原子性執(zhí)行命令
SET lKey randId NX PX 30000

randId是由客戶端生成的一個隨機(jī)字符串,該客戶端加鎖時具有唯一性,主要是為了避免釋放別人的鎖。

我們來看這么一樣流程,如下圖:

圖片圖片

  1. 1. Client1 獲取鎖成功。
  2. 2. 由于Client1 業(yè)務(wù)處理時間過長, 鎖過期時間到了,鎖自動釋放了
  3. 3. Client2 獲取到了對應(yīng)同一個資源的鎖。
  4. 4. Client1 業(yè)務(wù)處理完成,釋放鎖,但是釋放掉了Client2 持有的鎖。
  5. 5. 而Client3此時還能獲得鎖,同樣Client2此時持有鎖,都亂套了。??

而這個randId就可以在釋放鎖的時候避免了釋放別人的鎖,因?yàn)樵卺尫沛i的時候,Client需要先獲取到該鎖的值(randId),判斷是否相同后才能刪除。

if (redis.get(lKey).equals(randId)) {
    redis.del(lockKey);
}

加鎖的時候需要原子性,釋放鎖的時候該怎么做到原子性???

這個問題很好,我們在加鎖的時候通過原子性命令避免了潛在的設(shè)置過期時間失敗問題,釋放鎖同樣是Get + Del兩條命令,這里同樣存在釋放別人鎖的問題。

腦瓜嗡嗡的??,咋那么多需要考慮的問題呀,看累了休息會??,咋們繼續(xù)往下看!

這里問題的根源在于:鎖的判斷在客戶端,釋放在服務(wù)端,如下圖:

圖片圖片

所以 應(yīng)該將鎖的判斷和刪除都在redis服務(wù)端進(jìn)行,可以借助lua腳本保證原子性,釋放鎖的核心邏輯【GET、判斷、DEL】,寫成 Lua 腳,讓Redis執(zhí)行,這樣實(shí)現(xiàn)能保證這三步的原子性。

// 判斷鎖是自己的,才釋放
if redis.call("GET",KEYS[1]) == ARGV[1]
then
    return redis.call("DEL",KEYS[1])
else
    return 0
end

如果Client1獲取到鎖后,因?yàn)闃I(yè)務(wù)問題需要較長的處理時間,超過了鎖過期時間,該怎么辦?

既然業(yè)務(wù)執(zhí)行時間超過了鎖過期時間,那么我們可以給鎖續(xù)期呀,比如開啟一個守護(hù)進(jìn)程,定時監(jiān)測鎖的失效時間,在快要過期的時候,對鎖進(jìn)行自動續(xù)期,重新設(shè)置過期時間。

Redisson框架中就實(shí)現(xiàn)了這個,就要WatchDog(看門狗):加鎖時沒有指定加鎖時間時會啟用 watchdog 機(jī)制,默認(rèn)加鎖 30 秒,每 10 秒鐘檢查一次,如果存在就重新設(shè)置 過期時間為 30 秒(即 30 秒之后它就不再續(xù)期了)

圖片圖片

嗯嗯,這應(yīng)該就比較穩(wěn)健了吧!??

嘿嘿,以上這些都是鎖在「單個」Redis 實(shí)例中可能產(chǎn)生的問題,確實(shí)單節(jié)點(diǎn)分布式鎖能解決大部分人的需求。但是通常都是用【Redis Cluster】或者【哨兵模式】這兩種方式實(shí)現(xiàn) Redis 的高可用,這就有主從同步問題發(fā)生。??

試想這樣的場景:

  1. 1. Client1請求Master加鎖成功
  2. 2. 然而Master異常宕機(jī),加鎖信息還未同步到從庫上(主從復(fù)制是異步的)
  3. 3. 此時從庫Slave1被哨兵提升為新主庫,鎖信息不在新的主庫上(未同步到Slave1)

圖片圖片

面對這種問題,Redis 的作者提出一種解決方 Redlock, 是基于多個 Redis 節(jié)點(diǎn)(都是 Master)的一種實(shí)現(xiàn),該方案基于 2 個前提:

  1. 1. 不再需要部署從庫和哨兵實(shí)例,只部署主庫
  2. 2. 但主庫要部署多個,官方推薦至少 5 個實(shí)例

Redlock加鎖流程:

1. Client先獲取「當(dāng)前時間戳T1」

2. Client依次向這 5 個 Redis 實(shí)例發(fā)起加鎖請求(用前面講到的 SET 命令),且每個請求會設(shè)置超時時間(毫秒級,要遠(yuǎn)小于鎖的有效時間),如果某一個實(shí)例加鎖失敗(包括網(wǎng)絡(luò)超時、鎖被其它人持有等各種異常情況),就立即向下一個 Redis 實(shí)例申請加鎖

3. 如果Client從 >=3 個(大多數(shù))以上 Redis 實(shí)例加鎖成功,則再次獲取「當(dāng)前時間戳T2」,如果 T2 - T1 < 鎖的過期時間,此時,認(rèn)為客戶端加鎖成功,否則認(rèn)為加鎖失敗

4. 加鎖成功,去操作共享資源(例如修改 MySQL 某一行,或發(fā)起一個 API 請求)

5. 加鎖失敗,Client向「全部節(jié)點(diǎn)」發(fā)起釋放鎖請求(前面講到的 Lua 腳本釋放鎖)

Redlock釋放鎖:

客戶端向所有 Redis 節(jié)點(diǎn)發(fā)起釋放鎖的操作

圖片圖片

問題 1:為什么要在多個實(shí)例上加鎖?

本質(zhì)上為了容錯,我們看圖中的多個Master示例節(jié)點(diǎn),實(shí)際夠構(gòu)成了一個分布式系統(tǒng),分布式系統(tǒng)中總會有異常節(jié)點(diǎn),多個實(shí)例加鎖的話,即使部分實(shí)例異常宕機(jī),剩余的實(shí)例加鎖成功,整個鎖服務(wù)依舊可用!

問題 2:為什么步驟 3 加鎖成功后,還要計(jì)算加鎖的累計(jì)耗時?

加鎖操作的針對的是分布式中的多個節(jié)點(diǎn),所以耗時肯定是比單個實(shí)例耗時更,還要考慮網(wǎng)絡(luò)延遲、丟包、超時等情況發(fā)生,網(wǎng)絡(luò)請求次數(shù)越多,異常的概率越大。

所以即使 N/2+1 個節(jié)點(diǎn)加鎖成功,但如果加鎖的累計(jì)耗時已經(jīng)超過了鎖的過期時間,那么此時的鎖已經(jīng)沒有意義了

問題 3:為什么釋放鎖,要操作所有節(jié)點(diǎn)?

主要是為了保證清除節(jié)點(diǎn)異常情況導(dǎo)致殘留的鎖!

比如:在某一個 Redis 節(jié)點(diǎn)加鎖時,可能因?yàn)椤妇W(wǎng)絡(luò)原因」導(dǎo)致加鎖失敗。

或者客戶端在一個 Redis 實(shí)例上加鎖成功,但在讀取響應(yīng)結(jié)果時,網(wǎng)絡(luò)問題導(dǎo)致讀取失敗,那這把鎖其實(shí)已經(jīng)在 Redis 上加鎖成功了。

所以說釋放鎖的時候,不管以前有沒有加鎖成功,都要釋放所有節(jié)點(diǎn)的鎖。

這里有一個關(guān)于Redlock安全性的爭論,這里就一筆帶過吧,大家有興趣可以去看看:

Java面試365:RedLock紅鎖安全性爭論(上)4 贊同 · 0 評論文章

圖片圖片

基于Etcd

Etcd是一個Go語言實(shí)現(xiàn)的非常可靠的kv存儲系統(tǒng),常在分布式系統(tǒng)中存儲著關(guān)鍵的數(shù)據(jù),通常應(yīng)用在配置中心、服務(wù)發(fā)現(xiàn)與注冊、分布式鎖等場景。

本文主要從分布式鎖的角度來看Etcd是如何實(shí)現(xiàn)分布式鎖的,Let's Go !

Etcd特性介紹:

  • ? Lease機(jī)制:即租約機(jī)制(TTL,Time To Live),etcd可以為存儲的kv對設(shè)置租約,當(dāng)租約到期,kv將失效刪除;同時也支持續(xù)約,keepalive
  • ? Revision機(jī)制:每個key帶有一個Revision屬性值,etcd每進(jìn)行一次事務(wù)對應(yīng)的全局Revision值都會+1,因此每個key對應(yīng)的Revision屬性值都是全局唯一的。通過比較Revision的大小就可以知道進(jìn)行寫操作的順序
  • ? 在實(shí)現(xiàn)分布式鎖時,多個程序同時搶鎖,根據(jù)Revision值大小依次獲得鎖,避免“驚群效應(yīng)”,實(shí)現(xiàn)公平鎖
  • ? Prefix機(jī)制:也稱為目錄機(jī)制,可以根據(jù)前綴獲得該目錄下所有的key及其對應(yīng)的屬性值
  • ? Watch機(jī)制:watch支持watch某個固定的key或者一個前綴目錄,當(dāng)watch的key發(fā)生變化,客戶端將收到通知

為什么這些特性就可以讓Etcd實(shí)現(xiàn)分布式鎖呢?因?yàn)镋tcd這些特性可以滿足實(shí)現(xiàn)分布式鎖的以下要求:

  • ? 租約機(jī)制(Lease):用于支撐異常情況下的鎖自動釋放能力
  • ? 前綴和 Revision 機(jī)制:用于支撐公平獲取鎖和排隊(duì)等待的能力
  • ? 監(jiān)聽機(jī)制(Watch):用于支撐搶鎖能力
  • ? 集群模式:用于支撐鎖服務(wù)的高可用

有了這些知識理論我們一起看看Etcd是怎么實(shí)現(xiàn)分布式鎖的,因?yàn)槲易约阂彩荊olang開發(fā),這里我們也放一些代碼。

先看流程,再結(jié)合代碼注釋!

圖片圖片

func main() {
    config := clientv3.Config{
        Endpoints:   []string{"xxx.xxx.xxx.xxx:2379"},
        DialTimeout: 5 * time.Second,
    }
 
    // 獲取客戶端連接
    client, err := clientv3.New(config)
    if err != nil {
        fmt.Println(err)
        return
    }
 
    // 1. 上鎖(創(chuàng)建租約,自動續(xù)租,拿著租約去搶占一個key )
    // 用于申請租約
    lease := clientv3.NewLease(client)
 
    // 申請一個10s的租約
    leaseGrantResp, err := lease.Grant(context.TODO(), 10) //10s
    if err != nil {
        fmt.Println(err)
        return
    }
 
    // 拿到租約的id
    leaseID := leaseGrantResp.ID
 
    // 準(zhǔn)備一個用于取消續(xù)租的context
    ctx, cancelFunc := context.WithCancel(context.TODO())
 
    // 確保函數(shù)退出后,自動續(xù)租會停止
    defer cancelFunc()
        // 確保函數(shù)退出后,租約會失效
    defer lease.Revoke(context.TODO(), leaseID)
 
    // 自動續(xù)租
    keepRespChan, err := lease.KeepAlive(ctx, leaseID)
    if err != nil {
        fmt.Println(err)
        return
    }
 
    // 處理續(xù)租應(yīng)答的協(xié)程
    go func() {
        select {
        case keepResp := <-keepRespChan:
            if keepRespChan == nil {
                fmt.Println("lease has expired")
                goto END
            } else {
                // 每秒會續(xù)租一次
                fmt.Println("收到自動續(xù)租應(yīng)答", keepResp.ID)
            }
        }
    END:
    }()
 
    // if key 不存在,then設(shè)置它,else搶鎖失敗
    kv := clientv3.NewKV(client)
    // 創(chuàng)建事務(wù)
    txn := kv.Txn(context.TODO())
    // 如果key不存在
    txn.If(clientv3.Compare(clientv3.CreateRevision("/cron/lock/job7"), "=", 0)).
        Then(clientv3.OpPut("/cron/jobs/job7", "", clientv3.WithLease(leaseID))).
        Else(clientv3.OpGet("/cron/jobs/job7")) //如果key存在
 
    // 提交事務(wù)
    txnResp, err := txn.Commit()
    if err != nil {
        fmt.Println(err)
        return
    }
 
    // 判斷是否搶到了鎖
    if !txnResp.Succeeded {
        fmt.Println("鎖被占用了:", string(txnResp.Responses[0].GetResponseRange().Kvs[0].Value))
        return
    }
 
    // 2. 處理業(yè)務(wù)(鎖內(nèi),很安全)
 
    fmt.Println("處理任務(wù)")
    time.Sleep(5 * time.Second)
 
    // 3. 釋放鎖(取消自動續(xù)租,釋放租約)
    // defer會取消續(xù)租,釋放鎖
}

不過clientv3提供的concurrency包也實(shí)現(xiàn)了分布式鎖,我們可以更便捷的實(shí)現(xiàn)分布式鎖,不過內(nèi)部實(shí)現(xiàn)邏輯差不多:

  1. 1. 首先concurrency.NewSession方法創(chuàng)建Session對象
  2. 2. 然后Session對象通過concurrency.NewMutex 創(chuàng)建了一個Mutex對象
  3. 3. 加鎖和釋放鎖分別調(diào)用Lock和UnLock

基于ZooKeeper

ZooKeeper 的數(shù)據(jù)存儲結(jié)構(gòu)就像一棵樹,這棵樹由節(jié)點(diǎn)組成,這種節(jié)點(diǎn)叫做 Znode

加鎖/釋放鎖的過程是這樣的

圖片圖片

1. Client嘗試創(chuàng)建一個 znode 節(jié)點(diǎn),比如/lock,比如Client1先到達(dá)就創(chuàng)建成功了,相當(dāng)于拿到了鎖

2. 其它的客戶端會創(chuàng)建失?。▃node 已存在),獲取鎖失敗。

3. Client2可以進(jìn)入一種等待狀態(tài),等待當(dāng)/lock 節(jié)點(diǎn)被刪除的時候,ZooKeeper 通過 watch 機(jī)制通知它

4. 持有鎖的Client1訪問共享資源完成后,將 znode 刪掉,鎖釋放掉了

5. Client2繼續(xù)完成獲取鎖操作,直到獲取到鎖為止

ZooKeeper不需要考慮過期時間,而是用【臨時節(jié)點(diǎn)】,Client拿到鎖之后,只要連接不斷,就會一直持有鎖。即使Client崩潰,相應(yīng)臨時節(jié)點(diǎn)Znode也會自動刪除,保證了鎖釋放。

Zookeeper 是怎么檢測這個客戶端是否崩潰的呢?

每個客戶端都與 ZooKeeper 維護(hù)著一個 Session,這個 Session 依賴定期的心跳(heartbeat)來維持。

如果 Zookeeper 長時間收不到客戶端的心跳,就認(rèn)為這個 Session 過期了,也會把這個臨時節(jié)點(diǎn)刪除。

當(dāng)然這也并不是完美的解決方案

以下場景中Client1和Client2在窗口時間內(nèi)可能同時獲得鎖:

1. Client 1 創(chuàng)建了 znode 節(jié)點(diǎn)/lock,獲得了鎖。

2. Client 1 進(jìn)入了長時間的 GC pause。(或者網(wǎng)絡(luò)出現(xiàn)問題、或者 zk 服務(wù)檢測心跳線程出現(xiàn)問題等等)

3. Client 1 連接到 ZooKeeper 的 Session 過期了。znode 節(jié)點(diǎn)/lock 被自動刪除。

4. Client 2 創(chuàng)建了 znode 節(jié)點(diǎn)/lock,從而獲得了鎖。

5. Client 1 從 GC pause 中恢復(fù)過來,它仍然認(rèn)為自己持有鎖。

好,現(xiàn)在我們來總結(jié)一下 Zookeeper 在使用分布式鎖時優(yōu)劣:

Zookeeper 的優(yōu)點(diǎn):

  1. 1. 不需要考慮鎖的過期時間,使用起來比較方便
  2. 2. watch 機(jī)制,加鎖失敗,可以 watch 等待鎖釋放,實(shí)現(xiàn)樂觀鎖

缺點(diǎn):

  1. 1. 性能不如 Redis
  2. 2. 部署和運(yùn)維成本高
  3. 3. 客戶端與 Zookeeper 的長時間失聯(lián),鎖被釋放問題

總結(jié)

文章內(nèi)容比較多,涉及到的知識點(diǎn)也很多,如果看一遍沒理解,那么建議你收藏一下多讀幾遍,構(gòu)建好對于分布式鎖你的情景結(jié)構(gòu)。

總結(jié)一下吧,本文主要總結(jié)了分布式鎖和使用方式,實(shí)現(xiàn)分布式鎖可以有多種方式。

數(shù)據(jù)庫:通過創(chuàng)建一條唯一記錄來表示一個鎖,唯一記錄添加成功,鎖就創(chuàng)建成功,釋放鎖的話需要刪除記錄,但是很容易出現(xiàn)性能瓶頸,因此基本上不會使用數(shù)據(jù)庫作為分布式鎖。

Redis:Redis提供了高效的獲取鎖和釋放鎖的操作,而且結(jié)合Lua腳本,Redission等,有比較好的異常情況處理方式,因?yàn)槭腔趦?nèi)存的,讀寫效率也是非常高。

Etcd:利用租約(Lease),Watch,Revision機(jī)制,提供了一種簡單實(shí)現(xiàn)的分布式鎖方式,集群模式讓Etcd能處理大量讀寫,性能出色,但是配置復(fù)雜,一致性問題也存在。

Zookeeper:利用ZooKeeper提供的節(jié)點(diǎn)同步功能來實(shí)現(xiàn)分布式鎖,而且不用設(shè)置過期時間,可以自動的處理異常情況下的鎖釋放。

如果你的業(yè)務(wù)數(shù)據(jù)非常敏感,在使用分布式鎖時,一定要注意這個問題,不能假設(shè)分布式鎖 100% 安全。

當(dāng)然也需要結(jié)合自己的業(yè)務(wù),可能大多數(shù)情況下我們還是使用Redis作為分布式鎖,一個是我們比較熟悉,然后性能和處理異常情況也有較多方式,我覺得滿足大多數(shù)業(yè)務(wù)場景就可以了。

責(zé)任編輯:武曉燕 來源: 小許code
相關(guān)推薦

2024-05-31 08:00:00

2016-03-03 17:42:10

DockerDCOS

2018-10-12 09:42:00

分布式鎖 Java多線

2025-02-08 12:05:44

MySQLMyISAMInnoDB

2021-10-21 06:52:17

ZooKeeper分布式配置

2019-08-01 11:04:10

Linux磁盤I

2024-11-28 15:11:28

2018-03-05 11:29:17

云計(jì)算云服務(wù)服務(wù)器

2021-03-04 10:20:41

運(yùn)維工程師互聯(lián)網(wǎng)

2021-10-09 11:34:59

MySQL分布式鎖庫存

2019-06-19 15:40:06

分布式鎖RedisJava

2021-02-28 07:49:28

Zookeeper分布式

2017-01-16 14:13:37

分布式數(shù)據(jù)庫

2018-04-03 16:24:34

分布式方式

2017-04-13 10:51:09

Consul分布式

2022-04-08 08:27:08

分布式鎖系統(tǒng)

2019-02-26 09:51:52

分布式鎖RedisZookeeper

2023-08-21 19:10:34

Redis分布式

2022-01-06 10:58:07

Redis數(shù)據(jù)分布式鎖

2021-10-25 10:21:59

ZK分布式鎖ZooKeeper
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號