日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
看一眼就能懂的“分布式鎖”原理

看一眼就能懂的“分布式鎖”原理

作者:賈俊江 2019-06-10 08:04:26

開發(fā)

架構(gòu)

開發(fā)工具

分布式 分布式鎖和我們平常講到的鎖原理基本一樣,目的就是確保在多個線程并發(fā)時,只有一個線程在同一刻操作這個業(yè)務(wù)或者說方法、變量。

武進(jìn)網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)建站!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站開發(fā)等網(wǎng)站項目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)建站自2013年起到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運(yùn)維經(jīng)驗,來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)建站

 分布式鎖和我們平常講到的鎖原理基本一樣,目的就是確保在多個線程并發(fā)時,只有一個線程在同一刻操作這個業(yè)務(wù)或者說方法、變量。

在一個進(jìn)程中,也就是一個 JVM 或者說應(yīng)用中,我們很容易去處理控制,在 jdk java.util 并發(fā)包中已經(jīng)為我們提供了這些方法去加鎖,比如 Synchronized 關(guān)鍵字或者 Lock 鎖,都可以處理。

但是我們現(xiàn)在的應(yīng)用程序如果只部署一臺服務(wù)器,那并發(fā)量是很差的,如果同時有上萬的請求,很有可能造成服務(wù)器壓力過大而癱瘓。

想想雙十一和大年三十晚上十點(diǎn),瓜分支付寶紅包等業(yè)務(wù)場景,自然需要用到多臺服務(wù)器去同時處理這些業(yè)務(wù),這些服務(wù)可能會有上百臺同時處理。

但是我們想一想,如果有 100 臺服務(wù)器要處理分紅包的業(yè)務(wù),現(xiàn)在假設(shè)有 1 億的紅包,1 千萬個人分,金額隨機(jī),那么這個業(yè)務(wù)場景下,是不是必須確保這 1 千萬個人***分的紅包金額總和等于 1 億?

如果處理不好~~每人分到 100 萬,那馬云爸爸估計大年初一,就得宣布破產(chǎn)了~~

常規(guī)鎖會造成什么情況?

首先說一下我們?yōu)槭裁匆慵骸:唵卫斫饩褪?,需求?請求并發(fā)量)變大了,一個工人處理能力有限,那就多招一些工人來一起處理。

假設(shè) 1 千萬個請求平均分配到 100 臺服務(wù)器上,每個服務(wù)器接收 10w 的請求。

這 10w 個請求并不是在同一秒中來的,可能是在 1-2 個小時內(nèi),可以聯(lián)想下我們?nèi)砩祥_紅包,等到 10:20 開始,有的人立馬開了,有的人等到 12 點(diǎn)才想起來。

那這樣的話,平均到每一秒上的請求也就不到 1 千個,這種壓力一般的服務(wù)器還是可以承受的:

  • ***個用戶來分,請求到來后,需要在 1 億里面給他分一部分錢,金額隨機(jī),假設(shè)***個人分到了 100,那就要在這 1 億中減去 100 塊,剩下 99999900 塊。
  • 第二個用戶再來分,金額隨機(jī),這次分 200 塊,那就需要在剩下的 99999900 塊中再減去 200 塊,剩下 99999700 塊。
  • 等到第 10w 個用戶來,一看還有 1000w,那這 1000w 全成他的了。

等于是在每個服務(wù)器中去分 1 億,也就是 10w 個用戶分了 1 億,***總計有 100 個服務(wù)器,要分 100 億。

如果真這樣了,雖說馬云爸爸不會破產(chǎn)(據(jù)***統(tǒng)計馬云有 2300 億人民幣),那分紅包的開發(fā)項目組,以及產(chǎn)品經(jīng)理,可以 GG了~

簡化結(jié)構(gòu)圖如下:

分布式鎖怎么去處理?

那么為了解決這個問題,讓 1000 萬用戶只分 1 億,而不是 100 億,這個時候分布式鎖就派上用處了。

分布式鎖可以把整個集群就當(dāng)作是一個應(yīng)用一樣去處理,那么也就需要這個鎖獨(dú)立于每一個服務(wù)之外,而不是在服務(wù)里面。

假設(shè)***個服務(wù)器接收到用戶 1 的請求后,不能只在自己的應(yīng)用中去判斷還有多少錢可以分了,而需要去外部請求專門負(fù)責(zé)管理這 1 億紅包的人(服務(wù)),問他:哎,我這里要分 100 塊,給我 100。

管理紅包的妹子(服務(wù))一看,還有 1 個億,那好,給你 100 塊,然后剩下 99999900 塊。

第二個請求到來后,被服務(wù)器 2 獲取,繼續(xù)去詢問,管理紅包的妹子,我這邊要分 10 塊,管理紅包的妹子先查了下還有 99999900,那就說:好,給你 10 塊,那就剩下 99999890 塊。

等到第 1000w 個請求到來后,服務(wù)器 100 拿到請求,繼續(xù)去詢問,管理紅包的妹子,我要 100,妹子翻了翻白眼,對你說,就剩 1 塊了,愛要不要,那這個時候就只能給你 1 塊了(1 塊也是錢啊,買根辣條還是可以的)。

這些請求編號 1,2 不代表執(zhí)行的先后順序,正式的場景下,應(yīng)該是 100 臺服務(wù)器每個服務(wù)器持有一個請求去訪問負(fù)責(zé)管理紅包的妹子(服務(wù))。

那在管紅包的妹子那里同時會接收到 100 個請求,這個時候就需要在負(fù)責(zé)紅包的妹子那里加個鎖就可以了(拋繡球),你們 100 個服務(wù)器誰拿到鎖(搶到繡球),誰就進(jìn)來和我談,我給你分,其他人就等著去吧。

經(jīng)過上面的分布式鎖的處理后,馬云爸爸終于放心了,決定給紅包團(tuán)隊每人加一個雞腿。

簡化的結(jié)構(gòu)圖如下:

分布式鎖的實(shí)現(xiàn)有哪些?

說到分布式鎖的實(shí)現(xiàn),還是有很多的,有數(shù)據(jù)庫方式的,有 Redis 分布式鎖,有 Zookeeper 分布式鎖等等。

我們?nèi)绻捎?Redis 作為分布式鎖,那么上圖中負(fù)責(zé)“紅包的妹子(服務(wù))”,就可以替換成 Redis,請自行腦補(bǔ)。

①為什么 Redis 可以實(shí)現(xiàn)分布式鎖?

首先 Redis 是單線程的,這里的單線程指的是網(wǎng)絡(luò)請求模塊使用了一個線程(所以不需考慮并發(fā)安全性),即一個線程處理所有網(wǎng)絡(luò)請求,其他模塊仍用了多個線程。

在實(shí)際的操作中過程大致是這樣子的:服務(wù)器 1 要去訪問發(fā)紅包的妹子,也就是 Redis,那么它會在 Redis 中通過"setnx key value" 操作設(shè)置一個 Key 進(jìn)去,Value 是啥不重要,重要的是要有一個 Key,也就是一個標(biāo)記。

而且這個 Key 你愛叫啥叫啥,只要所有的服務(wù)器設(shè)置的 Key 相同就可以。

假設(shè)我們設(shè)置一個,如下圖:

那么我們可以看到會返回一個 1,那就代表了成功。

如果再來一個請求去設(shè)置同樣的 Key,如下圖:

這個時候會返回 0,那就代表失敗了。

那么我們就可以通過這個操作去判斷是不是當(dāng)前可以拿到鎖,或者說可以去訪問“負(fù)責(zé)發(fā)紅包的妹子”,如果返回 1,那我就開始去執(zhí)行后面的邏輯,如果返回 0,那就說明已經(jīng)被人占用了,我就要繼續(xù)等待。

當(dāng)服務(wù)器 1 拿到鎖之后,進(jìn)行了業(yè)務(wù)處理,完成后,還需要釋放鎖,如下圖所示:

刪除成功返回 1,那么其他的服務(wù)器就可以繼續(xù)重復(fù)上面的步驟去設(shè)置這個 Key,以達(dá)到獲取鎖的目的。

當(dāng)然以上的操作是在 Redis 客戶端直接進(jìn)行的,通過程序調(diào)用的話,肯定就不能這么寫,比如 Java 就需要通過 Jedis 去調(diào)用,但是整個處理邏輯基本都是一樣的。

通過上面的方式,我們好像是解決了分布式鎖的問題,但是想想還有沒有什么問題呢?

對,問題還是有的,可能會有死鎖的問題發(fā)生,比如服務(wù)器 1 設(shè)置完之后,獲取了鎖之后,忽然發(fā)生了宕機(jī)。

那后續(xù)的刪除 Key 操作就沒法執(zhí)行,這個 Key 會一直在 Redis 中存在,其他服務(wù)器每次去檢查,都會返回 0,他們都會認(rèn)為有人在使用鎖,我需要等。

為了解決這個死鎖的問題,我們就需要給 Key 設(shè)置有效期了。設(shè)置的方式有 2 種:

***種就是在 Set 完 Key 之后,直接設(shè)置 Key 的有效期 "expire key timeout" ,為 Key 設(shè)置一個超時時間,單位為 Second,超過這個時間鎖會自動釋放,避免死鎖。

這種方式相當(dāng)于,把鎖持有的有效期,交給了 Redis 去控制。如果時間到了,你還沒有給我刪除 Key,那 Redis 就直接給你刪了,其他服務(wù)器就可以繼續(xù)去 Setnx 獲取鎖。

第二種方式,就是把刪除 Key 權(quán)利交給其他的服務(wù)器,那這個時候就需要用到 Value 值了,比如服務(wù)器 1,設(shè)置了 Value 也就是 Timeout 為當(dāng)前時間 +1 秒 。

這個時候服務(wù)器 2 通過 Get 發(fā)現(xiàn)時間已經(jīng)超過系統(tǒng)當(dāng)前時間了,那就說明服務(wù)器 1 沒有釋放鎖,服務(wù)器 1 可能出問題了,服務(wù)器 2 就開始執(zhí)行刪除 Key 操作,并且繼續(xù)執(zhí)行 Setnx 操作。

但是這塊有一個問題,也就是不光你服務(wù)器 2 可能會發(fā)現(xiàn)服務(wù)器 1 超時了,服務(wù)器 3 也可能會發(fā)現(xiàn),如果剛好服務(wù)器 2 Setnx 操作完成,服務(wù)器 3 就接著刪除,是不是服務(wù)器 3 也可以 Setnx 成功了?

那就等于是服務(wù)器 2 和服務(wù)器 3 都拿到鎖了,那就問題大了。這個時候怎么辦呢?

這個時候需要用到“GETSET key value”命令了。這個命令的意思就是獲取當(dāng)前 Key 的值,并且設(shè)置新的值。

假設(shè)服務(wù)器 2 發(fā)現(xiàn) Key 過期了,開始調(diào)用 getset 命令,然后用獲取的時間判斷是否過期,如果獲取的時間仍然是過期的,那就說明拿到鎖了。

如果沒有,則說明在服務(wù) 2 執(zhí)行 getset 之前,服務(wù)器 3 可能也發(fā)現(xiàn)鎖過期了,并且在服務(wù)器 2 之前執(zhí)行了 getset 操作,重新設(shè)置了過期時間。

那么服務(wù)器 2 就需要放棄后續(xù)的操作,繼續(xù)等待服務(wù)器 3 釋放鎖或者去監(jiān)測 Key 的有效期是否過期。

這塊其實(shí)有一個小問題是,服務(wù)器 3 已經(jīng)修改了有效期,拿到鎖之后,服務(wù)器 2 也修改了有效期,但是沒能拿到鎖。

但是這個有效期的時間已經(jīng)被在服務(wù)器 3 的基礎(chǔ)上又增加一些,但是這種影響其實(shí)還是很小的,幾乎可以忽略不計。

②為什么 Zookeeper 可實(shí)現(xiàn)分布式鎖?

百度百科是這么介紹的:ZooKeeper 是一個分布式的,開放源碼的分布式應(yīng)用程序協(xié)調(diào)服務(wù),是 Google 的 Chubby 一個開源的實(shí)現(xiàn),是 Hadoop 和 Hbase 的重要組件。

那對于我們初次認(rèn)識的人,可以理解成 ZooKeeper 就像是我們的電腦文件系統(tǒng),我們可以在 d 盤中創(chuàng)建文件夾 a,并且可以繼續(xù)在文件夾 a 中創(chuàng)建文件夾 a1,a2。

那我們的文件系統(tǒng)有什么特點(diǎn)?那就是同一個目錄下文件名稱不能重復(fù),同樣 ZooKeeper 也是這樣的。

在 ZooKeeper 所有的節(jié)點(diǎn),也就是文件夾稱作 Znode,而且這個 Znode 節(jié)點(diǎn)是可以存儲數(shù)據(jù)的。

我們可以通過“ create /zkjjj nice”來創(chuàng)建一個節(jié)點(diǎn),這個命令就表示,在根目錄下創(chuàng)建一個 zkjjj 的節(jié)點(diǎn),值是 nice。

同樣這里的值,和我在前面說的 Redis 中的一樣,沒什么意義,你隨便給。

另外 ZooKeeper 可以創(chuàng)建 4 種類型的節(jié)點(diǎn),分別是:

  • 持久性節(jié)點(diǎn)
  • 持久性順序節(jié)點(diǎn)
  • 臨時性節(jié)點(diǎn)
  • 臨時性順序節(jié)點(diǎn)

首先說下持久性節(jié)點(diǎn)和臨時性節(jié)點(diǎn)的區(qū)別:

  • 持久性節(jié)點(diǎn)表示只要你創(chuàng)建了這個節(jié)點(diǎn),那不管你 ZooKeeper 的客戶端是否斷開連接,ZooKeeper 的服務(wù)端都會記錄這個節(jié)點(diǎn)。
  • 臨時性節(jié)點(diǎn)剛好相反,一旦你 ZooKeeper 客戶端斷開了連接,那 ZooKeeper 服務(wù)端就不再保存這個節(jié)點(diǎn)。
  • 順便也說下順序性節(jié)點(diǎn),順序性節(jié)點(diǎn)是指,在創(chuàng)建節(jié)點(diǎn)的時候,ZooKeeper 會自動給節(jié)點(diǎn)編號比如 0000001,0000002 這種的。

Zookeeper 有一個監(jiān)聽機(jī)制,客戶端注冊監(jiān)聽它關(guān)心的目錄節(jié)點(diǎn),當(dāng)目錄節(jié)點(diǎn)發(fā)生變化(數(shù)據(jù)改變、被刪除、子目錄節(jié)點(diǎn)增加刪除)等,Zookeeper 會通知客戶端。

在 Zookeeper 中如何加鎖?

下面我們繼續(xù)結(jié)合我們上面的分紅包場景,描述下在 Zookeeper 中如何加鎖。

假設(shè)服務(wù)器 1,創(chuàng)建了一個節(jié)點(diǎn) /zkjjj,成功了,那服務(wù)器 1 就獲取了鎖,服務(wù)器 2 再去創(chuàng)建相同的鎖,就會失敗,這個時候就只能監(jiān)聽這個節(jié)點(diǎn)的變化。

等到服務(wù)器 1 處理完業(yè)務(wù),刪除了節(jié)點(diǎn)后,他就會得到通知,然后去創(chuàng)建同樣的節(jié)點(diǎn),獲取鎖處理業(yè)務(wù),再刪除節(jié)點(diǎn),后續(xù)的 100 臺服務(wù)器與之類似。

注意這里的 100 臺服務(wù)器并不是挨個去執(zhí)行上面的創(chuàng)建節(jié)點(diǎn)的操作,而是并發(fā)的,當(dāng)服務(wù)器 1 創(chuàng)建成功,那么剩下的 99 個就都會注冊監(jiān)聽這個節(jié)點(diǎn),等通知,以此類推。

但是大家有沒有注意到,這里還是有問題的,還是會有死鎖的情況存在,對不對?

當(dāng)服務(wù)器 1 創(chuàng)建了節(jié)點(diǎn)后掛了,沒能刪除,那其他 99 臺服務(wù)器就會一直等通知,那就完蛋了。

這個時候就需要用到臨時性節(jié)點(diǎn)了,我們前面說過了,臨時性節(jié)點(diǎn)的特點(diǎn)是客戶端一旦斷開,就會丟失。

也就是當(dāng)服務(wù)器 1 創(chuàng)建了節(jié)點(diǎn)后,如果掛了,那這個節(jié)點(diǎn)會自動被刪除,這樣后續(xù)的其他服務(wù)器,就可以繼續(xù)去創(chuàng)建節(jié)點(diǎn),獲取鎖了。

但是我們可能還需要注意到一點(diǎn),就是驚群效應(yīng):舉一個很簡單的例子,當(dāng)你往一群鴿子中間扔一塊食物,雖然最終只有一個鴿子搶到食物,但所有鴿子都會被驚動來爭奪,沒有搶到…

就是當(dāng)服務(wù)器 1 節(jié)點(diǎn)有變化,會通知其余的 99 個服務(wù)器,但是最終只有 1 個服務(wù)器會創(chuàng)建成功,這樣 98 還是需要等待監(jiān)聽,那么為了處理這種情況,就需要用到臨時順序性節(jié)點(diǎn)。

大致意思就是,之前是所有 99 個服務(wù)器都監(jiān)聽一個節(jié)點(diǎn),現(xiàn)在就是每一個服務(wù)器監(jiān)聽自己前面的一個節(jié)點(diǎn)。

假設(shè) 100 個服務(wù)器同時發(fā)來請求,這個時候會在 /zkjjj 節(jié)點(diǎn)下創(chuàng)建 100 個臨時順序性節(jié)點(diǎn) /zkjjj/000000001,/zkjjj/000000002,一直到 /zkjjj/000000100,這個編號就等于是已經(jīng)給他們設(shè)置了獲取鎖的先后順序了。

當(dāng) 001 節(jié)點(diǎn)處理完畢,刪除節(jié)點(diǎn)后,002 收到通知,去獲取鎖,開始執(zhí)行,執(zhí)行完畢,刪除節(jié)點(diǎn),通知 003~以此類推。


當(dāng)前題目:看一眼就能懂的“分布式鎖”原理
分享鏈接:http://www.5511xx.com/article/djcogec.html