遠(yuǎn)程辦公”">國(guó)務(wù)院聯(lián)防聯(lián)控機(jī)制新聞發(fā)布會(huì),多次肯定“云辦公”、“遠(yuǎn)程辦公”
861
2025-04-04
1 秒殺場(chǎng)景下的數(shù)據(jù)一致性問題
某商品庫(kù)存10,A想買6,B想買5。
1.1 做夢(mèng)
A先買走6,庫(kù)存剩4,此時(shí)B應(yīng)該無法購(gòu)買5,給出數(shù)量不足提示
1.2 現(xiàn)實(shí)
AB獲取到商品都剩10,A買走6,在A更新庫(kù)存前,B又買走5,此時(shí)B更新庫(kù)存,商品還剩5。
1.3 想當(dāng)然地解決方案
給共享資源或?qū)蚕碣Y源的操作加鎖,來保證對(duì)資源的訪問互斥。利用ReentrantLcok或者synchronized即可。
但是在分布式系統(tǒng)中,由于分布式系統(tǒng)的分布性,這兩種鎖將失去原有鎖的效果。
必須使用分布式鎖。
2 分布式鎖的要求
獲取/釋放鎖的性能好
獲得鎖必須是原子性的
網(wǎng)絡(luò)抖動(dòng)或者宕機(jī)等原因?qū)е聼o法釋放鎖時(shí),鎖必須被清除,不然會(huì)發(fā)生死鎖
可重入
阻塞鎖和非阻塞鎖,阻塞鎖即沒有獲取到鎖,則繼續(xù)等待獲取鎖;非阻塞鎖即沒有獲取到鎖后,不繼續(xù)等待,直接返回鎖失敗。
3 分布式鎖實(shí)現(xiàn)方式
一、數(shù)據(jù)庫(kù)鎖
基于MySQL鎖表
完全依靠數(shù)據(jù)庫(kù)唯一索引來實(shí)現(xiàn),當(dāng)想要獲得鎖時(shí),即向數(shù)據(jù)庫(kù)中插入一條記錄,釋放鎖時(shí)就刪除這條記錄
這種方式存在以下問題:
鎖沒有失效時(shí)間,解鎖失敗會(huì)導(dǎo)致死鎖,其他線程無法再獲取到鎖,因?yàn)槲ㄒ凰饕齣nsert都會(huì)返回失敗
只能是非阻塞鎖,insert失敗直接就報(bào)錯(cuò)了,無法進(jìn)入隊(duì)列進(jìn)行重試
不可重入,同一線程在沒有釋放鎖之前無法再獲取到鎖
采用樂觀鎖
增加版本號(hào),根據(jù)版本號(hào)來判斷更新之前有沒有其他線程更新過,如果被更新過,則獲取鎖失敗
二、緩存鎖
這里主要是幾種基于redis的
基于setnx、expire
基于setnx(set if not exist)的特點(diǎn),當(dāng)緩存里key不存在時(shí),才會(huì)去set,否則直接返回false
如果返回true則獲取到鎖,否則獲取鎖失敗,為了防止死鎖,我們?cè)儆胑xpire命令對(duì)這個(gè)key設(shè)置一個(gè)超時(shí)時(shí)間來避免。
但是這里看似完美,實(shí)則有缺陷,當(dāng)我們setnx成功后,線程發(fā)生異常中斷,expire還沒來的及設(shè)置,那么就會(huì)產(chǎn)生死鎖。
解決上述問題有兩種方案
采用redis2.6.12版本以后的set,它提供了一系列選項(xiàng)
EX seconds – 設(shè)置鍵key的過期時(shí)間,單位時(shí)秒
PX milliseconds – 設(shè)置鍵key的過期時(shí)間,單位時(shí)毫秒
NX – 只有鍵key不存在的時(shí)候才會(huì)設(shè)置key的值
XX – 只有鍵key存在的時(shí)候才會(huì)設(shè)置key的值
第二種采用setnx(),get(),getset()
(1) 線程Asetnx,值為超時(shí)的時(shí)間戳(t1),如果返回true,獲得鎖。
(2) 線程B用get 命令獲取t1,與當(dāng)前時(shí)間戳比較,判斷是否超時(shí),沒超時(shí)false,如果已超時(shí)執(zhí)行步驟3
(3) 計(jì)算新的超時(shí)時(shí)間t2,使用getset命令返回t3(這個(gè)值可能其他線程已經(jīng)修改過),如果t1==t3,獲得鎖,如果t1!=t3說明鎖被其他線程獲取了
(4) 獲取鎖后,處理完業(yè)務(wù)邏輯,再去判斷鎖是否超時(shí),如果沒超時(shí)刪除鎖,如果已超時(shí),不用處理(防止刪除其他線程的鎖)
RedLock算法
zookeeper分布式鎖
zookeeper是一個(gè)為分布式應(yīng)用提供一致性服務(wù)的軟件,它內(nèi)部是一個(gè)分層的文件系統(tǒng)目錄樹結(jié)構(gòu),規(guī)定統(tǒng)一個(gè)目錄下只能有一個(gè)唯一文件名
數(shù)據(jù)模型
永久節(jié)點(diǎn)
節(jié)點(diǎn)創(chuàng)建后,不會(huì)因?yàn)闀?huì)話失效而消失
臨時(shí)節(jié)點(diǎn)
與永久節(jié)點(diǎn)相反,如果客戶端連接失效,則立即刪除節(jié)點(diǎn)
順序節(jié)點(diǎn)
與上述兩個(gè)節(jié)點(diǎn)特性類似,如果指定創(chuàng)建這類節(jié)點(diǎn)時(shí),zk會(huì)自動(dòng)在節(jié)點(diǎn)名后加一個(gè)數(shù)字后綴,并且是有序的
##監(jiān)視器(watcher):
當(dāng)創(chuàng)建一個(gè)節(jié)點(diǎn)時(shí),可以注冊(cè)一個(gè)該節(jié)點(diǎn)的監(jiān)視器,當(dāng)節(jié)點(diǎn)狀態(tài)發(fā)生改變時(shí),watch被觸發(fā)時(shí),ZooKeeper將會(huì)向客戶端發(fā)送且僅發(fā)送一條通知,因?yàn)閣atch只能被觸發(fā)一次
根據(jù)zookeeper的這些特性來實(shí)現(xiàn)分布式鎖
創(chuàng)建一個(gè)鎖目錄lock
希望獲得鎖的線程A就在lock目錄下,創(chuàng)建臨時(shí)順序節(jié)點(diǎn)
獲取鎖目錄下所有的子節(jié)點(diǎn),然后獲取比自己小的兄弟節(jié)點(diǎn),如果不存在,則說明當(dāng)前線程順序號(hào)最小,獲得鎖
線程B獲取所有節(jié)點(diǎn),判斷自己不是最小節(jié)點(diǎn),設(shè)置監(jiān)聽(watcher)比自己次小的節(jié)點(diǎn)(只關(guān)注比自己次小的節(jié)點(diǎn)是為了防止發(fā)生“羊群效應(yīng)”)
線程A處理完,刪除自己的節(jié)點(diǎn),線程B監(jiān)聽到變更事件,判斷自己是最小的節(jié)點(diǎn),獲得鎖。
小結(jié)
在分布式系統(tǒng)中,共享資源互斥訪問問題非常普遍,而針對(duì)訪問共享資源的互斥問題,常用的解決方案就是使用分布式鎖,這里只介紹了幾種常用的分布式鎖,分布式鎖的實(shí)現(xiàn)方式還有有很多種,根據(jù)業(yè)務(wù)選擇合適的分布式鎖
下面對(duì)上述幾種鎖進(jìn)行一下比較:
數(shù)據(jù)庫(kù)鎖
優(yōu)點(diǎn):直接使用數(shù)據(jù)庫(kù),使用簡(jiǎn)單。
缺點(diǎn):分布式系統(tǒng)大多數(shù)瓶頸都在數(shù)據(jù)庫(kù),使用數(shù)據(jù)庫(kù)鎖會(huì)增加數(shù)據(jù)庫(kù)負(fù)擔(dān)。
緩存鎖
優(yōu)點(diǎn):性能高,實(shí)現(xiàn)起來較為方便,在允許偶發(fā)的鎖失效情況,不影響系統(tǒng)正常使用,建議采用緩存鎖。
缺點(diǎn):通過鎖超時(shí)機(jī)制不是十分可靠,當(dāng)線程獲得鎖后,處理時(shí)間過長(zhǎng)導(dǎo)致鎖超時(shí),就失效了鎖的作用。
zookeeper鎖
優(yōu)點(diǎn):不依靠超時(shí)時(shí)間釋放鎖;可靠性高;系統(tǒng)要求高可靠性時(shí),建議采用zookeeper鎖。
缺點(diǎn):性能比不上緩存鎖,因?yàn)橐l繁的創(chuàng)建節(jié)點(diǎn)刪除節(jié)點(diǎn)。
Redis ZooKeeper 分布式
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(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)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。