Redlock分布式鎖
這篇文章主要是對 Redis 官方網(wǎng)站刊登的 Distributed locks with Redis 部分內(nèi)容的總結(jié)和翻譯。
什么是 RedLock
Redis 官方站這篇文章提出了一種權(quán)威的基于 Redis 實(shí)現(xiàn)分布式鎖的方式名叫 Redlock,此種方式比原先的單節(jié)點(diǎn)的方法更安全。它可以保證以下特性:
安全特性:互斥訪問,即永遠(yuǎn)只有一個(gè) client 能拿到鎖
避免死鎖:最終 client 都可能拿到鎖,不會(huì)出現(xiàn)死鎖的情況,即使原本鎖住某資源的 client crash 了或者出現(xiàn)了網(wǎng)絡(luò)分區(qū)
容錯(cuò)性:只要大部分 Redis 節(jié)點(diǎn)存活就可以正常提供服務(wù)
怎么在單節(jié)點(diǎn)上實(shí)現(xiàn)分布式鎖
SET resourcename myrandom_value NX PX 30000
主要依靠上述命令,該命令僅當(dāng) Key 不存在時(shí)(NX保證)set 值,并且設(shè)置過期時(shí)間 3000ms (PX保證),值 myrandomvalue 必須是所有 client 和所有鎖請求發(fā)生期間唯一的,釋放鎖的邏輯是:
if?redis.call("get",KEYS[1])?==?ARGV[1]?then????return?redis.call("del",KEYS[1])else????return?0end
上述實(shí)現(xiàn)可以避免釋放另一個(gè)client創(chuàng)建的鎖,如果只有 del 命令的話,那么如果 client1 拿到 lock1 之后因?yàn)槟承┎僮髯枞撕荛L時(shí)間,此時(shí) Redis 端 lock1 已經(jīng)過期了并且已經(jīng)被重新分配給了 client2,那么 client1 此時(shí)再去釋放這把鎖就會(huì)造成 client2 原本獲取到的鎖被 client1 無故釋放了,但現(xiàn)在為每個(gè) client 分配一個(gè) unique 的 string 值可以避免這個(gè)問題。至于如何去生成這個(gè) unique string,方法很多隨意選擇一種就行了。
Redlock 算法
算法很易懂,起 5 個(gè) master 節(jié)點(diǎn),分布在不同的機(jī)房盡量保證可用性。為了獲得鎖,client 會(huì)進(jìn)行如下操作:
得到當(dāng)前的時(shí)間,微妙單位
嘗試順序地在 5 個(gè)實(shí)例上申請鎖,當(dāng)然需要使用相同的 key 和 random value,這里一個(gè) client 需要合理設(shè)置與 master 節(jié)點(diǎn)溝通的 timeout 大小,避免長時(shí)間和一個(gè) fail 了的節(jié)點(diǎn)浪費(fèi)時(shí)間
當(dāng) client 在大于等于 3 個(gè) master 上成功申請到鎖的時(shí)候,且它會(huì)計(jì)算申請鎖消耗了多少時(shí)間,這部分消耗的時(shí)間采用獲得鎖的當(dāng)下時(shí)間減去第一步獲得的時(shí)間戳得到,如果鎖的持續(xù)時(shí)長(lock validity time)比流逝的時(shí)間多的話,那么鎖就真正獲取到了。
如果鎖申請到了,那么鎖真正的 lock validity time 應(yīng)該是 origin(lock validity time) - 申請鎖期間流逝的時(shí)間
如果 client 申請鎖失敗了,那么它就會(huì)在少部分申請成功鎖的 master 節(jié)點(diǎn)上執(zhí)行釋放鎖的操作,重置狀態(tài)
失敗重試
如果一個(gè) client 申請鎖失敗了,那么它需要稍等一會(huì)在重試避免多個(gè) client 同時(shí)申請鎖的情況,最好的情況是一個(gè) client 需要幾乎同時(shí)向 5 個(gè) master 發(fā)起鎖申請。另外就是如果 client 申請鎖失敗了它需要盡快在它曾經(jīng)申請到鎖的 master 上執(zhí)行 unlock 操作,便于其他 client 獲得這把鎖,避免這些鎖過期造成的時(shí)間浪費(fèi),當(dāng)然如果這時(shí)候網(wǎng)絡(luò)分區(qū)使得 client 無法聯(lián)系上這些 master,那么這種浪費(fèi)就是不得不付出的代價(jià)了。
放鎖
放鎖操作很簡單,就是依次釋放所有節(jié)點(diǎn)上的鎖就行了
性能、崩潰恢復(fù)和 fsync
如果我們的節(jié)點(diǎn)沒有持久化機(jī)制,client 從 5 個(gè) master 中的 3 個(gè)處獲得了鎖,然后其中一個(gè)重啟了,這是注意 整個(gè)環(huán)境中又出現(xiàn)了 3 個(gè) master 可供另一個(gè) client 申請同一把鎖! 違反了互斥性。如果我們開啟了 AOF 持久化那么情況會(huì)稍微好轉(zhuǎn)一些,因?yàn)?Redis 的過期機(jī)制是語義層面實(shí)現(xiàn)的,所以在 server 掛了的時(shí)候時(shí)間依舊在流逝,重啟之后鎖狀態(tài)不會(huì)受到污染。但是考慮斷電之后呢,AOF部分命令沒來得及刷回磁盤直接丟失了,除非我們配置刷回策略為 fsnyc = always,但這會(huì)損傷性能。解決這個(gè)問題的方法是,當(dāng)一個(gè)節(jié)點(diǎn)重啟之后,我們規(guī)定在 max TTL 期間它是不可用的,這樣它就不會(huì)干擾原本已經(jīng)申請到的鎖,等到它 crash 前的那部分鎖都過期了,環(huán)境不存在歷史鎖了,那么再把這個(gè)節(jié)點(diǎn)加進(jìn)來正常工作。
分布式 Redis
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。