电竞比分网-中国电竞赛事及体育赛事平台

分享

Redis實(shí)現(xiàn)分布式鎖

 python_lover 2022-02-12

一、分布式鎖

在分布式系統(tǒng)中,當(dāng)有多個(gè)客戶端(跨進(jìn)程,機(jī)器)需要獲取鎖時(shí)候,就需要分布式鎖,這個(gè)鎖保存在一個(gè)共享的存儲(chǔ)系統(tǒng)中。

redis就是一個(gè)可以被多個(gè)客戶端共享訪問(wèn)的存儲(chǔ)系統(tǒng),可以用來(lái)保存分布式鎖,并且redis支持?jǐn)?shù)萬(wàn)的并非操作,讀寫(xiě)性能高,可以適應(yīng)高并發(fā)的鎖操作場(chǎng)景。

報(bào)文主要討論兩種類型的redis鎖實(shí)現(xiàn):

  1. 基于單個(gè)redis節(jié)點(diǎn)的分布式鎖
  2. 基于多個(gè)redis節(jié)點(diǎn)的分布式鎖(共享存儲(chǔ)高可靠)、

二、基于單個(gè)redis節(jié)點(diǎn)的分布式鎖

背景

  1. 鎖變量名:lock_key。
  2. value=1:鎖被持有。
  3. value=0:鎖被釋放。

2.1 示意圖

加鎖示意圖

釋放鎖示意

2.2 redis命令支持

2.2.1 redis 命令支持-加鎖-setnx

加鎖需要三個(gè)操作

  1. 讀取鎖變量
  2. 判斷鎖變量的值
  3. 把鎖變量設(shè)置為1

以上三個(gè)操作需要在一個(gè)原子操作里面完成,可以通過(guò)lua腳本來(lái)實(shí)現(xiàn),但是redis提供了命令

setnx 可以用這個(gè)命令代替上面的三個(gè)操作,同時(shí)實(shí)現(xiàn)了原子操作。

setnx用于設(shè)置鍵值對(duì)的值,在執(zhí)行時(shí)會(huì)判斷鍵是否存在,如果不存在,就創(chuàng)建鍵,同時(shí)設(shè)置值,如果存在,就不做任何設(shè)置。

最終可以通過(guò)setnx的返回值來(lái)判斷鎖是否被持有:

  1. 返回值1,表示拿到鎖
  2. 返回值0,標(biāo)識(shí)鎖失敗

2.2.2 redis 命令支持-釋放鎖-del

del命令刪除

2.2.3 setnx和del的組合的問(wèn)題

可以通過(guò)setnx和del的組合實(shí)現(xiàn)加鎖和釋放鎖的問(wèn)題,但是setnx和del的組合有兩個(gè)問(wèn)題。

  1. setnx有鎖永遠(yuǎn)無(wú)法釋放的問(wèn)題,考慮場(chǎng)景,客戶端A使用setnx拿到鎖以后,在執(zhí)行自己業(yè)務(wù)邏輯的時(shí)候發(fā)送了異常,最終一直沒(méi)有調(diào)用del來(lái)釋放鎖。鎖一直被這個(gè)客戶端A占用著,其他客戶端無(wú)法拿到鎖,著會(huì)嚴(yán)重影響業(yè)務(wù).

    這種問(wèn)題解決方案就是給setnx給一個(gè)默認(rèn)的過(guò)期時(shí)間,當(dāng)業(yè)務(wù)異常無(wú)法釋放鎖時(shí),通過(guò)默認(rèn)過(guò)期時(shí)間,redis會(huì)自動(dòng)把這個(gè)鎖置為無(wú)效,間接釋放了鎖。

  2. del操作最大的問(wèn)題是因?yàn)殒i對(duì)應(yīng)的key是已知的,會(huì)出現(xiàn)key被誤刪的case,客戶端A獲取到了鎖,在釋放之前被客戶端B給刪除了,這個(gè)時(shí)候鎖變成了空閑狀態(tài),客戶端C又拿到了鎖,最終的結(jié)果就是A和C同時(shí)拿到了鎖。

    這個(gè)問(wèn)題的核心就是只有加鎖著本身才允許解除這個(gè)鎖??梢栽阪i對(duì)應(yīng)的值上做文章,讓鎖的值每次在加鎖的時(shí)候都能唯一被識(shí)別(只有使用鎖的客戶端才能提供),比如加鎖客戶端所在的客戶端專有信息等
    每次解鎖的時(shí)候必須要傳入這個(gè)唯一標(biāo)識(shí)。

    1. 解鎖前先獲取鎖對(duì)應(yīng)的值
    2. 拿這個(gè)值和傳入的唯一標(biāo)識(shí)進(jìn)行比較,如果一樣才進(jìn)行解鎖,不一樣,則不允許解鎖
    3. 以上兩個(gè)操作必須是一個(gè)原子操作

2.2.4 setnx和del的問(wèn)題解決

分析了問(wèn)題后就可以針對(duì)性解決。

  1. setnx-鎖無(wú)法釋放問(wèn)題:redis提供了SET key value NX PX timeout。功能和setnx一樣,但是會(huì)在timeout毫秒后自動(dòng)過(guò)期,這就解決了客戶端宕機(jī)后沒(méi)有釋放的問(wèn)題。

  2. del-鎖被誤釋放問(wèn)題:首先按照上面分析的對(duì)value進(jìn)行維一值設(shè)定。然后釋放鎖采用lua腳本實(shí)現(xiàn)check和釋放功能。

    if redis.call("get",KEYS[1]) == ARGV[1] then //check
        return redis.call("del",KEYS[1])
    else
        return 0
    end
    

    KEYS[1]的值是鎖的名字,ARGV[1]的值是鎖對(duì)應(yīng)的維一值。只有維一值相等,才進(jìn)行釋放。

2.3 單節(jié)點(diǎn)redis鎖的問(wèn)題

以上分析了單點(diǎn)redis實(shí)現(xiàn)分布式鎖的原理以及方法,但是單點(diǎn)redis最大的問(wèn)題就是單點(diǎn)本身,當(dāng)出現(xiàn)故障宕機(jī)或者網(wǎng)絡(luò)問(wèn)題導(dǎo)致的服務(wù)不可達(dá)時(shí),整個(gè)分布式鎖服務(wù)是無(wú)法使用的。

為了解決單點(diǎn)問(wèn)題,redis有主從哨兵機(jī)制,當(dāng)主節(jié)點(diǎn)故障后,哨兵會(huì)做故障轉(zhuǎn)移,從節(jié)點(diǎn)升級(jí)為主節(jié)點(diǎn)持續(xù)提供服務(wù),但是由于主從復(fù)制是異步的,在下面場(chǎng)景下會(huì)出問(wèn)題:

  1. 客戶端A拿到了鎖
  2. 主節(jié)點(diǎn)的信息還沒(méi)有同步到從節(jié)點(diǎn),主節(jié)點(diǎn)宕機(jī)了
  3. 哨兵把從節(jié)點(diǎn)升級(jí)為主節(jié)點(diǎn)
  4. 新的主節(jié)點(diǎn)鎖是空閑的,沒(méi)有被占用
  5. 客戶端B請(qǐng)求鎖也拿到了鎖
  6. 出現(xiàn)了A、B兩個(gè)客戶端同時(shí)拿到鎖的場(chǎng)景。

為了解決這個(gè)問(wèn)題,redis引入了多節(jié)點(diǎn)分布式鎖。

三、基于多個(gè)redis節(jié)點(diǎn)的分布式鎖

多節(jié)點(diǎn)分布式鎖,在redis中叫Redlock(redis distribution lock)。其具體思想是引入N個(gè)redis節(jié)點(diǎn),讓客戶端像這個(gè)N個(gè)節(jié)點(diǎn)以此請(qǐng)求加鎖,如果客戶端能夠獲得(N/2+1)
以上的鎖,那么久可以認(rèn)為客戶端成功拿到樂(lè)鎖,加鎖成功,否則便認(rèn)為加鎖失敗。這樣即使有單個(gè)redis實(shí)例發(fā)生故障,以為鎖變量在其他實(shí)例上也有保存,客戶端任然可以獲得鎖。

具體步驟:

  1. 客戶端獲取當(dāng)前時(shí)間
  2. 客戶端依次按序像N個(gè)節(jié)點(diǎn)獲取鎖
    1. 要對(duì)加鎖操作本身設(shè)置一個(gè)超時(shí)時(shí)間,加鎖的超時(shí)時(shí)間應(yīng)該遠(yuǎn)遠(yuǎn)小于業(yè)務(wù)鎖的有效時(shí)間,一般幾十毫秒即可。如果一個(gè)客戶端超時(shí),redis可以繼續(xù)和下一個(gè)節(jié)點(diǎn)進(jìn)行請(qǐng)求加鎖。
  3. 完成和所有節(jié)點(diǎn)的加鎖操作后,客戶端計(jì)算加鎖的耗時(shí)
  4. 判斷是否加鎖成功判斷
    1. 獲取到至少n/2+1的鎖成功
    2. 加鎖的耗時(shí)小于鎖的有效時(shí)間
  5. 重新計(jì)算鎖的有效時(shí)間,鎖的最初有效時(shí)間減去加鎖的耗時(shí)。如果新的有效時(shí)間不夠完成共享數(shù)據(jù)的操作(就是鎖內(nèi)要做的事情),可以釋放鎖,以免操作沒(méi)有完成鎖卻失效了,會(huì)出現(xiàn)多個(gè)客戶端同時(shí)執(zhí)行,失去了排他性。
  6. 如果第四步驟的判斷是獲取鎖失敗,那么需要執(zhí)行取消鎖操作,針對(duì)所有節(jié)點(diǎn),包括那些加鎖失敗的節(jié)點(diǎn)(有些節(jié)點(diǎn)可能因?yàn)榫W(wǎng)絡(luò)原因,實(shí)際加鎖成功,但是返回客戶端是失?。?/li>

四、選擇

單節(jié)點(diǎn)redis的redis分布式鎖,相對(duì)比較簡(jiǎn)單,會(huì)出現(xiàn)偶爾的鎖失效,如果允許這種場(chǎng)景,可以采用。

如果業(yè)務(wù)對(duì)并發(fā)的結(jié)果要求非常嚴(yán)格,建議使用redlock,但是整體部署維護(hù)成本較高。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多