redis分布式鎖與zk分布式鎖的對比分析
在分布式環(huán)境下,傳統(tǒng)的jvm級(jí)別的鎖會(huì)失效,那么分布式鎖就是非常有必要的一個(gè)技術(shù),一般我們可以通過redis,zk等技術(shù)來實(shí)現(xiàn)我們的分布式鎖
redis實(shí)現(xiàn)分布式鎖
原理
我們都知道redis的處理讀寫請求是單線程的,這種情況就不會(huì)發(fā)生并發(fā)的問題,其實(shí)實(shí)現(xiàn)起來很簡單,就是使用redis的 setnx 命令實(shí)現(xiàn),該命令如果redis中存在當(dāng)前key,就會(huì)返回0,否者插入成功。
那么就可以獲取鎖的時(shí)候添加一個(gè)k-v值(任意的一個(gè)值)到redis,釋放鎖的時(shí)候就刪除,這樣就使用redis實(shí)現(xiàn)了一個(gè)分布式鎖,相當(dāng)于分布式中所謂的鎖概念其實(shí)就相當(dāng)一個(gè)redis或者zk中的一個(gè)值,有這個(gè)值就加鎖成功
能實(shí)現(xiàn)的鎖類型
1、普通的鎖
2、讀寫鎖:大致就是給當(dāng)前的key設(shè)置一個(gè)特定的值標(biāo)識(shí)當(dāng)前鎖是讀鎖還是寫鎖,讀與讀之間不互斥,相當(dāng)與就是沒有鎖,讀與寫,寫于寫之間進(jìn)行互斥,這個(gè)鎖的目的是為了解決緩存一致性的問題,這個(gè)問題下面來分析
3、紅鎖:底層原理涉及到redis半數(shù)寫入機(jī)制,針對主從架構(gòu)中主節(jié)點(diǎn)掛了但是數(shù)據(jù)還未同步到從節(jié)點(diǎn)的問題,實(shí)現(xiàn)的方式就是當(dāng)一半以上的節(jié)點(diǎn)都寫入成功了才返回給客戶端成功的提示,而不是主節(jié)點(diǎn)寫入成功就返回,但是這種情況下的效率比較慢
注意事項(xiàng)
1、死鎖的情況:出現(xiàn)死鎖的情況有以下幾種情
(1)應(yīng)用程序沒有正常的釋放鎖:比如程序拋出異常之類導(dǎo)致釋放鎖代碼沒有執(zhí)行;
解決方案:需要把釋放鎖的代碼寫在finally模塊里面。
(2)鎖還沒有釋放redis宕機(jī):這個(gè)時(shí)候本來應(yīng)該刪除的key因?yàn)閞edis服務(wù)停掉了導(dǎo)致刪除不成功,出現(xiàn)死鎖的問題
解決方案:給每一個(gè)key設(shè)置一個(gè)超時(shí)時(shí)間,超時(shí)了自動(dòng)清除。
2、鎖永久失效的情況:出現(xiàn)原因是因?yàn)楫?dāng)前線程A還沒有運(yùn)行完然后鎖因?yàn)檫^期時(shí)間的原因自動(dòng)刪除了,這個(gè)時(shí)候其他線程B又能拿到這個(gè)鎖在redis中創(chuàng)建一個(gè)對應(yīng)的k-v值,然后線程A執(zhí)行到釋放鎖的時(shí)候會(huì)刪除掉對應(yīng)key的值,這個(gè)時(shí)候刪除的值是線程B對應(yīng)的鎖,而不是線程A的,這樣在高并發(fā)的情況下就有可能導(dǎo)致鎖壓根不生效
解決方案:在進(jìn)行設(shè)值的時(shí)候,value值設(shè)置成能標(biāo)識(shí)當(dāng)前線程的一個(gè)值,比如在當(dāng)前線程中創(chuàng)建一個(gè)uuid,然后在釋放鎖的時(shí)候也要比較value值,相同的情況就表示是當(dāng)前線程對應(yīng)的鎖,允許釋放,否則不允許釋放。
3、會(huì)存在鎖提前釋放的問題:當(dāng)然這個(gè)問題也是引起上面第2個(gè)問題的根本原因,但是解決方案是不一樣的
解決方案:在獲得鎖之后,處理業(yè)務(wù)邏輯的過程中,新建一個(gè)timer來定時(shí)的去重置鎖的生命周期,當(dāng)然前提是當(dāng)前業(yè)務(wù)邏輯還在執(zhí)行,這個(gè)定時(shí)的頻率一般設(shè)置為鎖生命周期的1/3,redisson中的 **看門狗 **其實(shí)內(nèi)部就是這樣實(shí)現(xiàn)。
4、主從結(jié)構(gòu)中鎖丟失:上面 紅鎖 已經(jīng)說明了情況
zk實(shí)現(xiàn)分布式鎖
原理
zk實(shí)現(xiàn)分布式鎖的原理其實(shí)和redis很像,都是往里面插入對應(yīng)的值,通過zk的create命令來實(shí)現(xiàn),zk中的值是通過樹形結(jié)構(gòu),類似與文件夾的層級(jí)目錄一樣,如果當(dāng)前節(jié)點(diǎn)存在那么create命令就會(huì)執(zhí)行失敗,這種情況就代表其他的線程已經(jīng)獲取到了鎖,當(dāng)前線程通過get -w /xxx的命令對當(dāng)前鎖進(jìn)行監(jiān)聽,如果當(dāng)前鎖被其他線程釋放,那么當(dāng)前線程會(huì)重新參與競爭鎖
能實(shí)現(xiàn)的鎖類型
1、非公平鎖:就是通過create創(chuàng)建節(jié)點(diǎn),誰創(chuàng)建成功誰就獲得了鎖,其他鎖對這個(gè)節(jié)點(diǎn)進(jìn)行監(jiān)聽,當(dāng)釋放鎖的時(shí)候,所有線程又來競爭這個(gè)鎖,但是這種情況會(huì)引發(fā)羊群效應(yīng),就是當(dāng)一個(gè)節(jié)點(diǎn)被釋放的時(shí)候所有的線程都會(huì)來競爭,浪費(fèi)性能
2、公平鎖:通過zk的臨時(shí)有序節(jié)點(diǎn)來實(shí)現(xiàn),當(dāng)前線程創(chuàng)建一個(gè)臨時(shí)順序節(jié)點(diǎn),然后判斷當(dāng)前節(jié)點(diǎn)是不是最小的節(jié)點(diǎn),如果時(shí)就獲得鎖,如果不是那么就監(jiān)聽他的上一個(gè)節(jié)點(diǎn),等到釋放鎖的時(shí)候會(huì)通知后一個(gè)節(jié)點(diǎn),然后重復(fù)以上判斷,這個(gè)就是公平鎖的實(shí)現(xiàn)方案,這樣就可以避免羊群效應(yīng),減輕服務(wù)器的壓力,但是這種情況可能會(huì)發(fā)生幽靈節(jié)點(diǎn)的產(chǎn)生導(dǎo)致死鎖
幽靈節(jié)點(diǎn):就是客戶端發(fā)送創(chuàng)建命令之后,zk已經(jīng)成功創(chuàng)建,但是在響應(yīng)的時(shí)候發(fā)生了宕機(jī),這個(gè)時(shí)候客戶端以為沒有成功,但是服務(wù)器端實(shí)際上已經(jīng)有了,但是這個(gè)客戶端不知道,就不會(huì)去釋放,就造成了幽靈節(jié)點(diǎn),通過 Protection模式能夠避免這個(gè)問題,這個(gè)的本質(zhì)就是在節(jié)點(diǎn)前面加上一個(gè)唯一的標(biāo)識(shí),如uuid,人客戶端再次請求的時(shí)候會(huì)比較這個(gè)uuid,如果有就認(rèn)為創(chuàng)建成功了,使用curator的protection模式原理就是這樣的,一下附一張公平鎖實(shí)現(xiàn)原理圖:
3、讀寫鎖:實(shí)現(xiàn)原理和公平鎖差不多,只是在創(chuàng)建每一個(gè)節(jié)點(diǎn)的時(shí)候標(biāo)識(shí)當(dāng)前節(jié)點(diǎn)時(shí)讀鎖(加read標(biāo)識(shí))還是寫鎖(加write標(biāo)識(shí))
兩種鎖的對比
分布式系統(tǒng)中通常要考慮CAP的,一致性,可用性和分區(qū)容錯(cuò)性,很多場景下是很難同時(shí)保證CAP的,這個(gè)時(shí)候就得做出取舍,分布式鎖也是這樣的。
redis分布式鎖:
- 優(yōu)點(diǎn):性能高,能保證AP,保證其高可用,
- 缺點(diǎn):但是不能保證其一致性,原因就是在redis集群+主從的結(jié)構(gòu)中,數(shù)據(jù)是通過分片存儲(chǔ)的,但是這個(gè)時(shí)候當(dāng)一個(gè)master節(jié)點(diǎn)掛了之后,slave節(jié)點(diǎn)還未同步到master節(jié)點(diǎn)的數(shù)據(jù),導(dǎo)致數(shù)據(jù)丟失,萬一丟失的數(shù)據(jù)剛好是你的鎖,那么就有可能造成并發(fā)問題,所以不能保證強(qiáng)一致性,這種情況下可以通過redisson的紅鎖來解決,解決的原理其實(shí)就是redis的半數(shù)寫入機(jī)制,但是這樣完全降低了redis的性能,所以一般情況下是不采用的,zk其實(shí)能保證其一致性的原因就是其半數(shù)寫入機(jī)制加上其 leader選舉的邏輯實(shí)現(xiàn)
zk分布式鎖:
- 優(yōu)點(diǎn):能夠保證其一致性,每個(gè)節(jié)點(diǎn)的創(chuàng)建都會(huì)同時(shí)寫入leader和follwer節(jié)點(diǎn),半數(shù)以上寫入成功才返回,如果leader節(jié)點(diǎn)掛了之后選舉的流程會(huì)優(yōu)先選舉zxid(事務(wù)Id)最大的節(jié)點(diǎn),就是選數(shù)據(jù)最全的,又因?yàn)榘霐?shù)寫入的機(jī)制這樣就不會(huì)導(dǎo)致丟數(shù)據(jù)(ZAB協(xié)議)
- 缺點(diǎn):性能沒有redis高
以上已經(jīng)說明了兩種分布式鎖實(shí)現(xiàn)的方式及其原理,怎么選擇以實(shí)際業(yè)務(wù)為準(zhǔn)
緩存一致性:數(shù)據(jù)庫于緩存的結(jié)果不一樣
雙寫一致性問題(圖網(wǎng)上找的,懶得畫了)
讀寫一致性問題
解決方案:
(1)對于我們的用戶自己的訂單數(shù)據(jù),或者用戶信息數(shù)據(jù),或者說高并發(fā)場景下能容忍短時(shí)間的數(shù)據(jù)不一致,這些都可以采用添加過期時(shí)間(本來進(jìn)入到緩存的數(shù)據(jù)就不要求強(qiáng)一致性,這種方式能解決大多數(shù)的情況,這種方式比較推薦也最常用)
(2)延遲雙刪:就是在更新了數(shù)據(jù)庫之后等一小段時(shí)間再刪除緩存(這種的缺點(diǎn)就是對于非高并發(fā)的請求,出現(xiàn)緩存一致性的問題概率本來就不大,但是卻人為的降低了代碼的性能)
(3)采用我們r(jià)edis提供的分布式讀寫鎖,讀讀之間不阻塞,一旦有數(shù)據(jù)更新那么讀的操作就阻塞,等待更新+刪除緩存的操作結(jié)束之后再進(jìn)行讀,這個(gè)情況雖然能真正的保證數(shù)據(jù)的一致性,但是加鎖了之后會(huì)進(jìn)行阻塞,高并發(fā)情況下的性能就降低了
(4)使用阿里巴巴的cannal組件,類似于一個(gè)mysql的slave節(jié)點(diǎn),監(jiān)聽著mysql的binlog日志文件,有變化就會(huì)主動(dòng)的通知我們(推薦)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Redisson分布式限流的實(shí)現(xiàn)原理分析
這篇文章主要介紹了Redisson分布式限流的實(shí)現(xiàn)原理分析,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07詳解基于redis實(shí)現(xiàn)的四種常見的限流策略
限流算法在分布式領(lǐng)域是一個(gè)經(jīng)常被提起的話題,當(dāng)系統(tǒng)的處理能力有限時(shí), 如何阻止計(jì)劃外的請求繼續(xù)對系統(tǒng)施壓,這是一個(gè)需要重視的問題。除了控制流量,限流還有一個(gè)應(yīng)用目的是控制用戶行為,避免垃圾請求2021-06-06Redis概述及l(fā)inux安裝redis的詳細(xì)教程
這篇文章主要介紹了Redis概述及l(fā)inux安裝redis的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10redis key命名規(guī)范的設(shè)計(jì)
如果結(jié)構(gòu)規(guī)劃不合理、命令使用不規(guī)范,會(huì)造成系統(tǒng)性能達(dá)到瓶頸、活動(dòng)高峰系統(tǒng)可用性下降,也會(huì)增大運(yùn)維難度,本文主要介紹了redis key命名規(guī)范的設(shè)計(jì),感興趣的可以了解一下2024-03-03