分布式鎖技術(shù)實(shí)現(xiàn)原理_第1頁
分布式鎖技術(shù)實(shí)現(xiàn)原理_第2頁
分布式鎖技術(shù)實(shí)現(xiàn)原理_第3頁
分布式鎖技術(shù)實(shí)現(xiàn)原理_第4頁
分布式鎖技術(shù)實(shí)現(xiàn)原理_第5頁
已閱讀5頁,還剩12頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、 分布式鎖技術(shù)實(shí)現(xiàn)原理目 錄 TOC o 1-3 h z u HYPERLINK l _Toc526848738 1.分布式鎖概念 PAGEREF _Toc526848738 h 3 HYPERLINK l _Toc526848739 2.基于 Redis 實(shí)現(xiàn)的鎖服務(wù) PAGEREF _Toc526848739 h 3 HYPERLINK l _Toc526848740 3.基于ZooKeeper 實(shí)現(xiàn)的鎖服務(wù) PAGEREF _Toc526848740 h 5分布式鎖概念分布式鎖,是用來控制分布式系統(tǒng)中互斥訪問共享資源的一種手段,從而避免并行導(dǎo)致的結(jié)果不可控?;镜膶?shí)現(xiàn)原理和單進(jìn)程鎖是一致

2、的,通過一個(gè)共享標(biāo)識來確定唯一性,對共享標(biāo)識進(jìn)行修改時(shí)能夠保證原子性和和對鎖服務(wù)調(diào)用方的可見性。由于分布式環(huán)境需要考慮各種異常因素,為實(shí)現(xiàn)一個(gè)靠譜的分布式鎖服務(wù)引入了一定的復(fù)雜度。分布式鎖服務(wù)一般需要能夠保證以下幾點(diǎn)。同一時(shí)刻只能有一個(gè)線程持有鎖鎖能夠可重入不會發(fā)生死鎖具備阻塞鎖特性,且能夠及時(shí)從阻塞狀態(tài)被喚醒鎖服務(wù)保證高性能和高可用當(dāng)前使用較多的分布式鎖方案主要基于 Redis、ZooKeeper 提供的功能特性加以封裝來實(shí)現(xiàn)的,下面我們會簡要分析下這兩種鎖方案的處理流程以及它們各自的問題?;?Redis 實(shí)現(xiàn)的鎖服務(wù) 加鎖流程SET resource_name my_random_val

3、ue NX PX max-lock-time注:資源不存在時(shí)才能夠成功執(zhí)行 set 操作,用于保證鎖持有者的唯一性;同時(shí)設(shè)置過期時(shí)間用于防止死鎖;記錄鎖的持有者,用于防止解鎖時(shí)解掉了不符合預(yù)期的鎖。解鎖流程if redis.get(resource_name) = my_random_valuereturn redis.del(resource_name)else return 0注:使用 Lua 腳本保證獲取鎖的所有者、對比解鎖者是否所有者、解鎖是一個(gè)原子操作。該方案的問題在于:通過過期時(shí)間來避免死鎖,過期時(shí)間設(shè)置多長對業(yè)務(wù)來說往往比較頭疼,時(shí)間短了可能會造成:持有鎖的線程 A 任務(wù)還未處理

4、完成,鎖過期了,線程 B 獲得了鎖,導(dǎo)致同一個(gè)資源被 A、B 兩個(gè)線程并發(fā)訪問;時(shí)間長了會造成:持有鎖的進(jìn)程宕機(jī),造成其他等待獲取鎖的進(jìn)程長時(shí)間的無效等待。Redis 的主從異步復(fù)制機(jī)制可能丟失數(shù)據(jù),會出現(xiàn)如下場景:A 線程獲得了鎖,但鎖數(shù)據(jù)還未同步到 slave 上,master 掛了,slave 頂成主,線程 B 嘗試加鎖,仍然能夠成功,造成 A、B 兩個(gè)線程并發(fā)訪問同一個(gè)資源。基于ZooKeeper 實(shí)現(xiàn)的鎖服務(wù) 加鎖流程首先在/resource_name節(jié)點(diǎn)下創(chuàng)建臨時(shí)有序節(jié)點(diǎn) 。獲取當(dāng)前線程創(chuàng)建的節(jié)點(diǎn)及 /resource_name 目錄下的所有子節(jié)點(diǎn),確定當(dāng)前節(jié)點(diǎn)序號是否最小,是則加

5、鎖成功。否則監(jiān)聽序號較小的前一個(gè)節(jié)點(diǎn)。注:ZAB 一致性協(xié)議保證了鎖數(shù)據(jù)的安全性,不會因?yàn)閿?shù)據(jù)丟失造成多個(gè)鎖持有者;心跳?;顧C(jī)制解決死鎖問題,防止由于進(jìn)程掛掉或者僵死導(dǎo)致的鎖長時(shí)間被無效占用。具備阻塞鎖特性,并通過 Watch 機(jī)制能夠及時(shí)從阻塞狀態(tài)被喚醒。解鎖流程是刪除當(dāng)前線程創(chuàng)建的臨時(shí)接點(diǎn)。該方案的問題在于通過心跳?;顧C(jī)制解決死鎖會造成鎖的不安全性,可能會出現(xiàn)如下場景:持有鎖的線程 A 僵死或網(wǎng)絡(luò)故障,導(dǎo)致服務(wù)端長時(shí)間收不到來自客戶端的?;钚奶?,服務(wù)端認(rèn)為客戶端進(jìn)程不存活主動釋放鎖,線程 B 搶到鎖,線程 A 恢復(fù),同時(shí)有兩個(gè)線程訪問共享資源?;谏显V對現(xiàn)有鎖方案的討論,我們能看到,一個(gè)理

6、想的鎖設(shè)計(jì)目標(biāo)主要應(yīng)該解決如下問題:鎖數(shù)據(jù)本身的安全性。不發(fā)生死鎖。不會有多個(gè)線程同時(shí)持有相同的鎖。而為了實(shí)現(xiàn)不發(fā)生死鎖的目標(biāo),又需要引入一種機(jī)制,當(dāng)持有鎖的進(jìn)程因?yàn)殄礄C(jī)、GC 活者網(wǎng)絡(luò)故障等各種原因無法主動過釋放鎖時(shí),能夠有其他手段釋放掉鎖,主流的做法有兩種:鎖設(shè)置過期時(shí)間,過期之后 Server 端自動釋放鎖。對鎖的持有進(jìn)程進(jìn)行探活,發(fā)現(xiàn)持鎖進(jìn)程不存活時(shí) Server 端自動釋放。實(shí)際上不管采用哪種方式,都可能造成鎖的安全性被破壞,導(dǎo)致多個(gè)線程同時(shí)持有同一把鎖的情況出現(xiàn)。因此我們認(rèn)為鎖設(shè)計(jì)方案應(yīng)在預(yù)防死鎖和鎖的安全性上取得平衡,沒有一種方案能夠絕對意義上保證不發(fā)生死鎖并且是安全的。而鎖一般

7、的用途又可以分為兩種,實(shí)際應(yīng)用場景下,需要根據(jù)具體需求出發(fā),權(quán)衡各種因素,選擇合適的鎖服務(wù)實(shí)現(xiàn)模型。無論選擇哪一種模型,需要我們清楚地知道它在安全性上有哪些不足,以及它會帶來什么后果。為了效率,主要是避免一件事被重復(fù)的做多次,用于節(jié)省 IT 成本,即使鎖偶然失效,也不會造成數(shù)據(jù)錯誤,該種情況首要考慮的是如何防止死鎖。為了正確性,在任何情況下都要保證共享資源的互斥訪問,一旦發(fā)生就意味著數(shù)據(jù)可能不一致,造成嚴(yán)重的后果,該種情況首要考慮的是如何保證鎖的安全。下面主要介紹一下 SharkLock 的一些設(shè)計(jì)選擇。鎖信息設(shè)計(jì)如下lockBy:Client 唯一標(biāo)識。condition:Client 在加

8、鎖時(shí)傳給 Server,用于定義 Client 期望 Server 的行為方式。lockTime:加鎖時(shí)間。txID:全局自增 ID。lease:租約。如何保證鎖數(shù)據(jù)的可靠性 SharkLock 底層存儲使用的是 SharkStore,SharkStore 是一個(gè)分布式的持久化 Key-Value 存儲系統(tǒng)。采用多副本來保證數(shù)據(jù)安全,同時(shí)使用 raft 來保證各個(gè)副本之間的數(shù)據(jù)一致性。如何預(yù)防死鎖 Client 定時(shí)向 Server 發(fā)送心跳包,Server 收到心跳包之后,維護(hù) Server 端 Session 并立即回復(fù),Client 收到心跳包響應(yīng)后,維護(hù) Client 端 Sessio

9、n。心跳包同時(shí)承擔(dān)了延長 Session 租約的功能。當(dāng)鎖持有方發(fā)生故障時(shí),Server 會在 Session 租約到期后,自動刪除該 Client 持有的鎖,以避免鎖長時(shí)間無法釋放而導(dǎo)致死鎖。Client 會在 Session 租約到期后,進(jìn)行回調(diào),可選擇性的決策是否要結(jié)束對當(dāng)前持有資源的訪問。對于未設(shè)置過期的鎖,也就意味著無法通過租約自動釋放故障 Client 持有的鎖。因此額外提供了一種協(xié)商機(jī)制,在加鎖的時(shí)候傳遞一些 condition 到服務(wù)端,用于約定 Client 端期望 Server 端對異常情況的處理,包括什么情況下能夠釋放鎖。譬如可以通過這種機(jī)制實(shí)現(xiàn) Server 端在未收到

10、十個(gè)心跳請求后自動釋放鎖,Client 端在未收到五個(gè)心跳響應(yīng)后主動結(jié)束對共享資源的訪問。盡最大程度保證鎖被加鎖進(jìn)程主動釋放。進(jìn)程正常關(guān)閉時(shí)調(diào)用鉤子來嘗試釋放鎖未釋放的鎖信息寫文件,進(jìn)程重啟后讀取鎖信息,并嘗試釋放鎖。如何確保鎖的安全性 1. 盡量不打破誰加鎖誰解鎖的約束,盡最大程度保證鎖被加鎖進(jìn)程主動釋放。a)進(jìn)程正常關(guān)閉時(shí)調(diào)用鉤子來嘗試釋放鎖。b)未釋放的鎖信息寫文件,進(jìn)程重啟后讀取鎖信息,并嘗試釋放鎖。2. 依靠自動續(xù)約來維持鎖的持有狀態(tài),在正常情況下,客戶端可以持有鎖任意長的時(shí)間,這可以確保它做完所有需要的資源訪問操作之后再釋放鎖。一定程度上防止如下情況發(fā)生。a)線程 A 獲取鎖,進(jìn)行

11、資源訪問。b)鎖已經(jīng)過期,但 A 線程未執(zhí)行完成。c)線程 B 獲得了鎖,導(dǎo)致同時(shí)有兩個(gè)線程在訪問共享資源。3. 提供一種安全檢測機(jī)制,用于對安全性要求極高的業(yè)務(wù)場景。a)對于同一把鎖,每一次獲取鎖操作,都會得到一個(gè)全局增長的版本號。b)對外暴露檢測 API checkVersion(lock_name,version),用于檢測持鎖進(jìn)程的鎖是不是已經(jīng)被其他進(jìn)程搶占(鎖已經(jīng)有了更新的版本號)。c)加鎖成功的客戶端與后端資源服務(wù)器通信的時(shí)候可帶上版本號,后端資源服務(wù)器處理請求前,調(diào)用 checkVersion 去檢查鎖是否依然有效。有效則認(rèn)為此客戶端依舊是鎖的持有者,可以為其提供服務(wù)。d)該機(jī)制

12、能在一定程度上解決持鎖 A 線程發(fā)生故障,Server 主動釋放鎖,線程 B 獲取鎖成功,A 恢復(fù)了認(rèn)為自己仍舊持有鎖而發(fā)起修改資源的請求,會因?yàn)殒i的版本號已經(jīng)過期而失敗,從而保障了鎖的安全性。下面對 SharkLock 依賴的 SharkStore 做一個(gè)簡單的介紹。SharkStore 基本模塊 Master Server 集群分片路由等元數(shù)據(jù)管理、擴(kuò)容和 Failover 調(diào)度等。Data Server 數(shù)據(jù)存儲節(jié)點(diǎn),提供 RPC 服務(wù)訪問其上的 KV 數(shù)據(jù)。Gateway Server 網(wǎng)關(guān)節(jié)點(diǎn),負(fù)責(zé)用戶接入。Sharding SharkStore 采用多副本的形式來保證數(shù)據(jù)的可靠性和

13、高可用。同一份數(shù)據(jù)會存儲多份,少于一半的副本宕機(jī)仍然可以正常服務(wù)。 SharkStore 的數(shù)據(jù)分布如下圖所示。擴(kuò)容方案 當(dāng)某個(gè)分片的大小到達(dá)一定閾值,就會觸發(fā)分裂操作,由一個(gè)分片變成兩個(gè),以達(dá)到擴(kuò)容的目的。Dataserver 上 range 的 leader 自己觸發(fā)。 leader 維持寫入操作字節(jié)計(jì)數(shù),每到達(dá) check size 大小,就異步遍歷其負(fù)責(zé)范圍內(nèi)的數(shù)據(jù),計(jì)算大小并同時(shí)找出分裂時(shí)的中間 key 如果大小到達(dá) split size,向 master 發(fā)起 AskSplit 請求,同意后提交一個(gè)分裂命令。分裂命令也會通過 raft 復(fù)制到其他副本。本地分裂。分裂是一個(gè)本地操作,

14、在本地新建一個(gè) range,把原始 range 的部分?jǐn)?shù)據(jù)劃撥給新 range,原始 range 仍然保留,只是負(fù)責(zé)的范圍減半。分裂是一個(gè)輕量級的操作。Failover 方案 failover 以 range 的級別進(jìn)行。range 的 leader 定時(shí)向其他副本發(fā)送心跳,一段時(shí)間內(nèi)收不到副本的心跳回應(yīng),就判斷副本宕機(jī),通過 range 心跳上報(bào)給 master。由 master 發(fā)起 failover 調(diào)度。 Master 會先刪除宕機(jī)的副本然后選擇一個(gè)合適的新節(jié)點(diǎn),添加到 range 組內(nèi)之后通過 raft 復(fù)制協(xié)議來完成新節(jié)點(diǎn)的數(shù)據(jù)同步。Balance 方案 dataserver 上的

15、 range leader 會通過 range 心跳上報(bào)一些信息,每個(gè) dataserver 還會有一個(gè)節(jié)點(diǎn)級別的 Node 心跳。 Master 收集這些信息來執(zhí)行 balance 操作。Balance 通過在流量低的節(jié)點(diǎn)上增加副本,流量高的節(jié)點(diǎn)上減少副本促使整個(gè)集群比較均衡,維護(hù)集群的穩(wěn)定和性能。Raft實(shí)踐: MultiRaft 1. 心跳合并以目標(biāo) dataserver 為維度,合并 dataserver 上所有 Raft 心跳 心跳只攜帶 range ids,心跳只用來維護(hù) leader 的權(quán)威和副本健康檢測 range ids 的壓縮,比如差量 + 整型變長 Leader 類似跟蹤

16、復(fù)制進(jìn)度,跟蹤 follower commit 位置。2. 快照管理控制建立 ACK 機(jī)制,在對端處理能力之內(nèi)發(fā)送快照 ; 控制發(fā)送和應(yīng)用快照的并發(fā)度,以及限速 ; 減少對正常業(yè)務(wù)的沖擊。Raft 實(shí)踐 -PreVote。Raft 算法中,leader 收到來自其他成員 term 比較高的投票請求會退位變成 follower因此,在節(jié)點(diǎn)分區(qū)后重加入、網(wǎng)絡(luò)閃斷等異常情況下,日志進(jìn)度落后的副本發(fā)起選舉,但其本身并無法被選舉為 leader,導(dǎo)致集群在若干個(gè)心跳內(nèi)丟失 leader,造成性能波動 ;針對這種情況,在 raft 作者的博士論文中,提出了 prevote 算法: 在發(fā)起選舉前,先進(jìn)行一次

17、預(yù)選舉 Pre-Candidate, 如果預(yù)選舉時(shí)能得到大多數(shù)的投票,再增加 term,進(jìn)行正常的選舉。prevote 會導(dǎo)致選舉時(shí)間變長 (多了一輪 RPC),然而這個(gè)影響在實(shí)踐中是非常小的, 可以有利于集群的穩(wěn)定,是非常值得的實(shí)踐。Raft 實(shí)踐: NonVoter 一個(gè)新的 raft 成員加入后,其日志進(jìn)度為空 ; 新成員的加入可能會導(dǎo)致 quorum 增加,并且同時(shí)引入了一個(gè)進(jìn)度異常的副本 ; 新成員在跟上 leader 日志進(jìn)度之前,新寫入的日志都無法復(fù)制給它 ; 如果此時(shí)再有原集群內(nèi)一個(gè)成員宕機(jī), 很有可能導(dǎo)致集群內(nèi)可寫副本數(shù)到不到 quorum,使得集群變得不可寫。 很多 raft 的實(shí)現(xiàn)中,都會引入了一種特殊身份的 raft 成員 (non-voting 或者 lea

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論