同一個(gè)jvm里多個(gè)線程操作同一個(gè)有狀態(tài)的變量,可以通過JVM內(nèi)的鎖保證線程安全。
創(chuàng)新互聯(lián)公司專注于安陸網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供安陸營銷型網(wǎng)站建設(shè),安陸網(wǎng)站制作、安陸網(wǎng)頁設(shè)計(jì)、安陸網(wǎng)站官網(wǎng)定制、微信小程序服務(wù),打造安陸網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供安陸網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
如果是多個(gè)JVM操作同一個(gè)有狀態(tài)的變量,如何保證線程安全呢?
這時(shí)候就需要分布式鎖來發(fā)揮它的作用了
分布式系統(tǒng)往往業(yè)務(wù)流量比較大、并發(fā)較高,對分布式鎖的高可用和高性能有較高的要求。一般分布式鎖的方案需要滿足如下要求:
利用主鍵唯一的特性,如果有多個(gè)請求同時(shí)提交到數(shù)據(jù)庫的話,數(shù)據(jù)庫會保證只有一個(gè)插入操作可以成功,那么我們就可以認(rèn)為操作成功的那個(gè)線程獲得了該方法的鎖,當(dāng)方法執(zhí)行完畢之后,想要釋放鎖的話,刪除這條數(shù)據(jù)庫記錄即可
connection.commit()
操作來釋放鎖一般數(shù)據(jù)庫使用innodb存儲引擎,在插入數(shù)據(jù)的時(shí)候會加行級鎖。從而達(dá)到是并發(fā)請求按順序執(zhí)行的效果
更新數(shù)據(jù)的時(shí)候帶上指定版本號,如果被其他線程提前更新的版本號,則此次更新失敗
對數(shù)據(jù)庫表侵入較大,每個(gè)表需要增加version字段
高并發(fā)下存在很多更新失敗
原子命令:SET key value NX PX milliseconds
PX?milliseconds 過期時(shí)間,防止加鎖線程死掉不能解鎖。過期時(shí)間設(shè)置太短,可能加鎖線程還沒有執(zhí)行完正常邏輯,就到了過期時(shí)間
NX 如果沒有這個(gè)key則設(shè)置,存在key返回失敗
value 隨機(jī)值(一般用UUID),用來實(shí)現(xiàn)只能由加鎖線程解鎖
lua腳本實(shí)現(xiàn)get value,delete的操作。加鎖的時(shí)候設(shè)置的value是不會重復(fù)的隨機(jī)值,解鎖的時(shí)候必須UUID一致才能解鎖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.3.2</version>
</dependency>
在Redis的分布式環(huán)境中,我們假設(shè)有N個(gè)Redis master。這些節(jié)點(diǎn)完全互相獨(dú)立,不存在主從復(fù)制或者其他集群協(xié)調(diào)機(jī)制。我們確保將在N個(gè)實(shí)例上使用與在Redis單實(shí)例下相同方法獲取和釋放鎖。現(xiàn)在我們假設(shè)有5個(gè)Redis master節(jié)點(diǎn),同時(shí)我們需要在5臺服務(wù)器上面運(yùn)行這些Redis實(shí)例,這樣保證他們不會同時(shí)都宕掉。
假設(shè)有cluster-1,cluster-2,cluster-3總計(jì)3個(gè)cluster模式集群。如果要獲取分布式鎖,那么需要向這3個(gè)cluster集群通過EVAL命令執(zhí)行LUA腳本,需要3/2+1=2,即至少2個(gè)cluster集群響應(yīng)成功。set的value要具有唯一性,redisson的value通過UUID+threadId保證value的唯一性
1.獲取當(dāng)前時(shí)間(單位是毫秒)。
2.輪流用相同的key和隨機(jī)值在N個(gè)節(jié)點(diǎn)上請求鎖,在這一步里,客戶端在每個(gè)master上請求鎖時(shí),會有一個(gè)和總的鎖釋放時(shí)間相比小的多的超時(shí)時(shí)間。比如如果鎖自動(dòng)釋放時(shí)間是10秒鐘,那每個(gè)節(jié)點(diǎn)鎖請求的超時(shí)時(shí)間可能是5-50毫秒的范圍,這個(gè)可以防止一個(gè)客戶端在某個(gè)宕掉的master節(jié)點(diǎn)上阻塞過長時(shí)間,如果一個(gè)master節(jié)點(diǎn)不可用了,我們應(yīng)該盡快嘗試下一個(gè)master節(jié)點(diǎn)。
3.客戶端計(jì)算第二步中獲取鎖所花的時(shí)間,只有當(dāng)客戶端在大多數(shù)master節(jié)點(diǎn)上成功獲取了鎖(在這里是3個(gè)),而且總共消耗的時(shí)間不超過鎖釋放時(shí)間,這個(gè)鎖就認(rèn)為是獲取成功了。
4.如果鎖獲取成功了,那現(xiàn)在鎖自動(dòng)釋放時(shí)間就是最初的鎖釋放時(shí)間減去之前獲取鎖所消耗的時(shí)間。
5.如果鎖獲取失敗了,不管是因?yàn)楂@取成功的鎖不超過一半(N/2+1)還是因?yàn)榭傁臅r(shí)間超過了鎖釋放時(shí)間,客戶端都會到每個(gè)master節(jié)點(diǎn)上釋放鎖,即便是那些他認(rèn)為沒有獲取成功的鎖。
需要在所有節(jié)點(diǎn)都釋放鎖就行,不管之前有沒有在該節(jié)點(diǎn)獲取鎖成功。
客戶端如果沒有在多數(shù)節(jié)點(diǎn)獲取到鎖,一定要盡快在獲取鎖成功的節(jié)點(diǎn)上釋放鎖,這樣就沒必要等到key超時(shí)后才能重新獲取這個(gè)鎖
?開始之前,讓我們假設(shè)客戶端可以在大多數(shù)節(jié)點(diǎn)都獲取到鎖,這樣所有的節(jié)點(diǎn)都會包含一個(gè)有相同存活時(shí)間的key。但是需要注意的是,這個(gè)key是在不同時(shí)間點(diǎn)設(shè)置的,所以這些key也會在不同的時(shí)間超時(shí),但是我們假設(shè)最壞情況下第一個(gè)key是在T1時(shí)間設(shè)置的(客戶端連接到第一個(gè)服務(wù)器時(shí)的時(shí)間),最后一個(gè)key是在T2時(shí)間設(shè)置的(客戶端收到最后一個(gè)服務(wù)器返回結(jié)果的時(shí)間),從T2時(shí)間開始,我們可以確認(rèn)最早超時(shí)的key至少也會存在的時(shí)間為MIN_VALIDITY=TTL-(T2-T1)-CLOCK_DRIFT,TTL是鎖超時(shí)時(shí)間、(T2-T1)是最晚獲取到的鎖的耗時(shí),CLOCK_DRIFT是不同進(jìn)程間時(shí)鐘差異,這個(gè)是用來補(bǔ)償前面的(T2-T1)。其他的key都會在這個(gè)時(shí)間點(diǎn)之后才會超時(shí),所以我們可以確定這些key在這個(gè)時(shí)間點(diǎn)之前至少都是同時(shí)存在的。
如果一個(gè)客戶端獲取大多數(shù)節(jié)點(diǎn)鎖的耗時(shí)接近甚至超過鎖的最大有效時(shí)間時(shí)(就是我們?yōu)镾ET操作設(shè)置的TTL值),那么系統(tǒng)會認(rèn)為這個(gè)鎖是無效的同時(shí)會釋放這些節(jié)點(diǎn)上的鎖,所以我們僅僅需要考慮獲取大多數(shù)節(jié)點(diǎn)鎖的耗時(shí)小于有效時(shí)間的情況。在這種情況下,根據(jù)我們前面的證明,在MIN_VALIDITY時(shí)間內(nèi),沒有客戶端能重新獲取鎖成功,所以多個(gè)客戶端都能同時(shí)成功獲取鎖的結(jié)果,只會發(fā)生在多數(shù)節(jié)點(diǎn)獲取鎖的時(shí)間都大大超過TTL時(shí)間的情況下,實(shí)際上這種情況下這些鎖都會失效
利用臨時(shí)節(jié)點(diǎn)與 watch 機(jī)制。每個(gè)鎖占用一個(gè)普通節(jié)點(diǎn) /lock,當(dāng)需要獲取鎖時(shí)在 /lock 目錄下創(chuàng)建一個(gè)臨時(shí)節(jié)點(diǎn),創(chuàng)建成功則表示獲取鎖成功,失敗則 watch/lock 節(jié)點(diǎn),有刪除操作后再去爭鎖。臨時(shí)節(jié)點(diǎn)好處在于當(dāng)進(jìn)程掛掉后能自動(dòng)上鎖的節(jié)點(diǎn)自動(dòng)刪除即取消鎖。
所有取鎖失敗的進(jìn)程都監(jiān)聽父節(jié)點(diǎn),很容易發(fā)生羊群效應(yīng),即當(dāng)釋放鎖后所有等待進(jìn)程一起來創(chuàng)建節(jié)點(diǎn),并發(fā)量很大。
上鎖改為創(chuàng)建臨時(shí)有序節(jié)點(diǎn),每個(gè)上鎖的節(jié)點(diǎn)均能創(chuàng)建節(jié)點(diǎn)成功,只是其序號不同。只有序號最小的可以擁有鎖,如果這個(gè)節(jié)點(diǎn)序號不是最小的則 watch 序號比本身小的前一個(gè)節(jié)點(diǎn) (公平鎖)。
在鎖節(jié)點(diǎn)下創(chuàng)建臨時(shí)順序節(jié)點(diǎn)。讀節(jié)點(diǎn)為R+序號,寫節(jié)點(diǎn)為W+序號。創(chuàng)建完節(jié)點(diǎn)后,獲取所有子節(jié)點(diǎn),對鎖節(jié)點(diǎn)注冊子節(jié)點(diǎn)變更的watcher監(jiān)聽,確定自己的序號在所有子節(jié)點(diǎn)中的位置。對于讀請求,沒有比自己序號小的寫節(jié)點(diǎn),就表示獲得了共享鎖,執(zhí)行讀取邏輯。對于寫請求,如果自己不是序號最小的子節(jié)點(diǎn),就需要進(jìn)入等待。接收到watcher通知后,重復(fù)獲取鎖。
共享鎖羊群效應(yīng)。大量的watcher通知和子節(jié)點(diǎn)列表獲取,兩個(gè)操作重復(fù)運(yùn)行。集群規(guī)模比較大的情況下,會對zookeeper服務(wù)器造成巨大的性能影響和網(wǎng)絡(luò)沖擊
讀請求,監(jiān)聽比自己小的寫節(jié)點(diǎn)。寫請求,監(jiān)聽比自己小的最后一個(gè)節(jié)點(diǎn)。
網(wǎng)站欄目:分布式鎖都有哪些實(shí)現(xiàn)方案?
轉(zhuǎn)載來于:http://www.chinadenli.net/article38/peispp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、標(biāo)簽優(yōu)化、網(wǎng)站設(shè)計(jì)、電子商務(wù)、軟件開發(fā)、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)