成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

Redis怎么實現(xiàn)分布式鎖

這篇文章主要介紹“怎么實現(xiàn)分布式鎖”,在日常操作中,相信很多人在Redis怎么實現(xiàn)分布式鎖問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Redis怎么實現(xiàn)分布式鎖”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

這篇文章主要介紹“怎么實現(xiàn)分布式鎖”,在日常操作中,相信很多人在Redis怎么實現(xiàn)分布式鎖問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Redis怎么實現(xiàn)分布式鎖”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

Redis命令介紹

創(chuàng)新互聯(lián)建站2013年至今,先為吉隆等服務建站,吉隆等地企業(yè),進行企業(yè)商務咨詢服務。為吉隆企業(yè)網站制作PC+手機+微官網三網同步一站式服務解決您的所有建站問題。

使用Redis實現(xiàn)分布式鎖,有兩個重要函數(shù)需要介紹
SETNX命令(SET if Not eXists)
語法:
SETNX key value
功能:

當且僅當 key 不存在,將 key 的值設為 value ,并返回1;若給定的 key 已經存在,則 SETNX 不做任何動作,并返回0。
GETSET命令
語法:
GETSET key value
功能:

將給定 key 的值設為 value ,并返回 key 的舊值 (old value),當 key 存在但不是字符串類型時,返回一個錯誤,當key不存在時,返回nil。
GET命令
語法:
GET key
功能:

返回 key 所關聯(lián)的字符串值,如果 key 不存在那么返回特殊值 nil 。
DEL命令
語法:
DEL key [KEY …]
功能:

刪除給定的一個或多個 key ,不存在的 key 會被忽略。

兵貴精,不在多。分布式鎖,我們就依靠這四個命令。但在具體實現(xiàn),還有很多細節(jié),需要仔細斟酌,因為在分布式并發(fā)多進程中,任何一點出現(xiàn)差錯,都會導致死鎖,hold住所有進程。

加鎖實現(xiàn)
SETNX 可以直接加鎖操作,比如說對某個關鍵詞foo加鎖,客戶端可以嘗試

SETNX foo.lock <current unix time>
如果返回1,表示客戶端已經獲取鎖,可以往下操作,操作完成后,通過

DEL foo.lock
命令來釋放鎖。

如果返回0,說明foo已經被其他客戶端上鎖,如果鎖是非堵塞的,可以選擇返回調用。如果是堵塞調用調用,就需要進入以下個重試循環(huán),直至成功獲得鎖或者重試超時。理想是美好的,現(xiàn)實是殘酷的。僅僅使用SETNX加鎖帶有競爭條件的,在某些特定的情況會造成死鎖錯誤。

處理死鎖

在上面的處理方式中,如果獲取鎖的客戶端端執(zhí)行時間過長,進程被kill掉,或者因為其他異常崩潰,導致無法釋放鎖,就會造成死鎖。所以,需要對加鎖要做時效性檢測。因此,我們在加鎖時,把當前時間戳作為value存入此鎖中,通過當前時間戳和Redis中的時間戳進行對比,如果超過一定差值,認為鎖已經時效,防止鎖無限期的鎖下去,但是,在大并發(fā)情況,如果同時檢測鎖失效,并簡單粗暴的刪除死鎖,再通過SETNX上鎖,可能會導致競爭條件的產生,即多個客戶端同時獲取鎖。
C1獲取鎖,并崩潰。C2和C3調用SETNX上鎖返回0后,獲得foo.lock的時間戳,通過比對時間戳,發(fā)現(xiàn)鎖超時。
C2 向foo.lock發(fā)送DEL命令。
C2 向foo.lock發(fā)送SETNX獲取鎖。
C3 向foo.lock發(fā)送DEL命令,此時C3發(fā)送DEL時,其實DEL掉的是C2的鎖。

C3 向foo.lock發(fā)送SETNX獲取鎖。

此時C2和C3都獲取了鎖,產生競爭條件,如果在更高并發(fā)的情況,可能會有更多客戶端獲取鎖。所以,DEL鎖的操作,不能直接使用在鎖超時的情況下,幸好我們有GETSET方法,假設我們現(xiàn)在有另外一個客戶端C4,看看如何使用GETSET方式,避免這種情況產生。
C1獲取鎖,并崩潰。C2和C3調用SETNX上鎖返回0后,調用GET命令獲得foo.lock的時間戳T1,通過比對時間戳,發(fā)現(xiàn)鎖超時。
C4 向foo.lock發(fā)送GESET命令,
GETSET foo.lock <current unix time>

并得到foo.lock中老的時間戳T2
如果T1=T2,說明C4獲得時間戳。

如果T1!=T2,說明C4之前有另外一個客戶端C5通過調用GETSET方式獲取了時間戳,C4未獲得鎖。只能sleep下,進入下次循環(huán)中。
現(xiàn)在唯一的問題是,C4設置foo.lock的新時間戳,是否會對鎖產生影響。其實我們可以看到C4和C5執(zhí)行的時間差值極小,并且寫入foo.lock中的都是有效時間錯,所以對鎖并沒有影響。

為了讓這個鎖更加強壯,獲取鎖的客戶端,應該在調用關鍵業(yè)務時,再次調用GET方法獲取T1,和寫入的T0時間戳進行對比,以免鎖因其他情況被執(zhí)行DEL意外解開而不知。以上步驟和情況,很容易從其他參考資料中看到??蛻舳颂幚砗褪〉那闆r非常復雜,不僅僅是崩潰這么簡單,還可能是客戶端因為某些操作被阻塞了相當長時間,緊接著 DEL 命令被嘗試執(zhí)行(但這時鎖卻在另外的客戶端手上)。也可能因為處理不當,導致死鎖。還有可能因為sleep設置不合理,導致Redis在大并發(fā)下被壓垮。最為常見的問題還有

GET返回nil時應該走那種邏輯?
第一種走超時邏輯
C1客戶端獲取鎖,并且處理完后,DEL掉鎖,在DEL鎖之前。C2通過SETNX向foo.lock設置時間戳T0 發(fā)現(xiàn)有客戶端獲取鎖,進入GET操作。
C2 向foo.lock發(fā)送GET命令,獲取返回值T1(nil)。
C2 通過T0>T1+expire對比,進入GETSET流程。
C2 調用GETSET向foo.lock發(fā)送T0時間戳,返回foo.lock的原值T2

C2 如果T2=T1相等,獲得鎖,如果T2!=T1,未獲得鎖。
第二種情況走循環(huán)走setnx邏輯
C1客戶端獲取鎖,并且處理完后,DEL掉鎖,在DEL鎖之前。C2通過SETNX向foo.lock設置時間戳T0 發(fā)現(xiàn)有客戶端獲取鎖,進入GET操作。
C2 向foo.lock發(fā)送GET命令,獲取返回值T1(nil)。

C2 循環(huán),進入下一次SETNX邏輯

兩種邏輯貌似都是OK,但是從邏輯處理上來說,第一種情況存在問題。當GET返回nil表示,鎖是被刪除的,而不是超時,應該走SETNX邏輯加鎖。走第一種情況的問題是,正常的加鎖邏輯應該走SETNX,而現(xiàn)在當鎖被解除后,走的是GETST,如果判斷條件不當,就會引起死鎖,很悲催,我在做的時候就碰到了,具體怎么碰到的看下面的問題

GETSET返回nil時應該怎么處理?
C1和C2客戶端調用GET接口,C1返回T1,此時C3網絡情況更好,快速進入獲取鎖,并執(zhí)行DEL刪除鎖,C2返回T2(nil),C1和C2都進入超時處理邏輯。
C1 向foo.lock發(fā)送GETSET命令,獲取返回值T11(nil)。
C1 比對C1和C11發(fā)現(xiàn)兩者不同,處理邏輯認為未獲取鎖。
C2 向foo.lock發(fā)送GETSET命令,獲取返回值T22(C1寫入的時間戳)。

C2 比對C2和C22發(fā)現(xiàn)兩者不同,處理邏輯認為未獲取鎖。

此時C1和C2都認為未獲取鎖,其實C1是已經獲取鎖了,但是他的處理邏輯沒有考慮GETSET返回nil的情況,只是單純的用GET和GETSET值就行對比,至于為什么會出現(xiàn)這種情況?一種是多客戶端時,每個客戶端連接Redis的后,發(fā)出的命令并不是連續(xù)的,導致從單客戶端看到的好像連續(xù)的命令,到Redis server后,這兩條命令之間可能已經插入大量的其他客戶端發(fā)出的命令,比如DEL,SETNX等。第二種情況,多客戶端之間時間不同步,或者不是嚴格意義的同步。

時間戳的問題
我們看到foo.lock的value值為時間戳,所以要在多客戶端情況下,保證鎖有效,一定要同步各的時間,如果各服務器間,時間有差異。時間不一致的客戶端,在判斷鎖超時,就會出現(xiàn)偏差,從而產生競爭條件。

鎖的超時與否,嚴格依賴時間戳,時間戳本身也是有精度限制,假如我們的時間精度為秒,從加鎖到執(zhí)行操作再到解鎖,一般操作肯定都能在一秒內完成。這樣的話,我們上面的CASE,就很容易出現(xiàn)。所以,最好把時間精度提升到毫秒級。這樣的話,可以保證毫秒級別的鎖是安全的。

分布式鎖的問題
1:必要的超時機制:獲取鎖的客戶端一旦崩潰,一定要有過期機制,否則其他客戶端都降無法獲取鎖,造成死鎖問題。
2:分布式鎖,多客戶端的時間戳不能保證嚴格意義的一致性,所以在某些特定因素下,有可能存在鎖串的情況。要適度的機制,可以承受小概率的事件產生。
3:只對關鍵處理節(jié)點加鎖,良好的習慣是,把相關的資源準備好,比如連接數(shù)據(jù)庫后,調用加鎖機制獲取鎖,直接進行操作,然后釋放,盡量減少持有鎖的時間。
4:在持有鎖期間要不要CHECK鎖,如果需要嚴格依賴鎖的狀態(tài),最好在關鍵步驟中做鎖的CHECK檢查機制,但是根據(jù)我們的測試發(fā)現(xiàn),在大并發(fā)時,每一次CHECK鎖操作,都要消耗掉幾個毫秒,而我們的整個持鎖處理邏輯才不到10毫秒,玩客沒有選擇做鎖的檢查。
5:sleep學問,為了減少對Redis的壓力,獲取鎖嘗試時,循環(huán)之間一定要做sleep操作。但是sleep時間是多少是門學問。需要根據(jù)自己的Redis的QPS,加上持鎖處理時間等進行合理計算。

6:至于為什么不使用Redis的muti,expire,watch等機制,可以查一參考資料,找下原因。

鎖測試數(shù)據(jù)

未使用sleep

第一種,鎖重試時未做sleep。單次請求,加鎖,執(zhí)行,解鎖時間 

可以看到加鎖和解鎖時間都很快,當我們使用
ab -n1000 -c100 'http://sandbox6.wanke.etao.com/test/test_sequence.php?tbpm=t'

AB 并發(fā)100累計1000次請求,對這個方法進行壓測時。 
我們會發(fā)現(xiàn),獲取鎖的時間變成,同時持有鎖后,執(zhí)行時間也變成,而delete鎖的時間,將近10ms時間,為什么會這樣?
1:持有鎖后,我們的執(zhí)行邏輯中包含了再次調用Redis操作,在大并發(fā)情況下,Redis執(zhí)行明顯變慢。
2:鎖的刪除時間變長,從之前的0.2ms,變成9.8ms,性能下降近50倍。

在這種情況下,我們壓測的QPS為49,最終發(fā)現(xiàn)QPS和壓測總量有關,當我們并發(fā)100總共100次請求時,QPS得到110多。當我們使用sleep時

使用Sleep時

單次執(zhí)行請求時

我們看到,和不使用sleep機制時,性能相當。當時用相同的壓測條件進行壓縮時 

獲取鎖的時間明顯變長,而鎖的釋放時間明顯變短,僅是不采用sleep機制的一半。當然執(zhí)行時間變成就是因為,我們在執(zhí)行過程中,重新創(chuàng)建數(shù)據(jù)庫連接,導致時間變長的。同時我們可以對比下Redis的命令執(zhí)行壓力情況 

上圖中細高部分是為未采用sleep機制的時的壓測圖,矮胖部分為采用sleep機制的壓測圖,通上圖看到壓力減少50%左右,當然,sleep這種方式還有個缺點QPS下降明顯,在我們的壓測條件下,僅為35,并且有部分請求出現(xiàn)超時情況。不過綜合各種情況后,我們還是決定采用sleep機制,主要是為了防止在大并發(fā)情況下把Redis壓垮,很不行,我們之前碰到過,所以肯定會采用sleep機制。

網頁題目:Redis怎么實現(xiàn)分布式鎖
瀏覽路徑:http://jinyejixie.com/article18/sddsdp.html

成都網站建設公司_創(chuàng)新互聯(lián),為您提供網站收錄ChatGPT、做網站、App設計營銷型網站建設、云服務器

廣告

聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

外貿網站制作
临夏市| 启东市| 永州市| 徐州市| 东乡族自治县| 靖安县| 白河县| 盘锦市| 九龙县| 定西市| 鄂尔多斯市| 射洪县| 吴川市| 平果县| 阿坝县| 岳阳县| 梧州市| 清涧县| 申扎县| 岳阳县| 黎川县| 阜平县| 萨迦县| 许昌市| 阿鲁科尔沁旗| 金堂县| 南昌县| 大庆市| 天水市| 安远县| 通州市| 乐山市| 久治县| 漳浦县| 阳山县| 永昌县| 岢岚县| 沈阳市| 高邑县| 贡山| 额尔古纳市|