成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Redis應(yīng)用篇(眾星追月):分布式鎖

存儲 存儲軟件 分布式 Redis
由于分布式系統(tǒng)「多線程」、「多進(jìn)程」并且「分布在不同機(jī)器」上,這將使原單機(jī)并發(fā)控制鎖策略失效,為了解決這個問題就需要一種「跨JVM的互斥機(jī)制」來控制共享資源的訪問,這就得靠分布式鎖啦。

[[431326]]

話題引入

大家好,我是小龍。

之前在《吃透Redis系列》專欄發(fā)表了第一篇文章《Redis基礎(chǔ)篇(萬丈高樓平地起):核心底層數(shù)據(jù)結(jié)構(gòu)》簡單介紹了Redis,以及它的內(nèi)部組織形式、核心數(shù)據(jù)結(jié)構(gòu)與大致使用場景。還沒看到得同學(xué)可以回過頭看看。

接下來,我將繼續(xù)帶大家深入理解,本文將介紹Redis高頻使用的一個場景——「利用Redis實習(xí)分布式鎖」。

想必大家都知道,在遇到并發(fā)問題時,我們通常會使用鎖來解決并發(fā)問題。

這是,有同學(xué)可能說:“這個我會,不就用synchronized、Lock這些實現(xiàn)嗎?”

對,你說的不錯。但是你只說對了一半,在「傳統(tǒng)單機(jī)部署」的情況下,可以使用Java并發(fā)處理相關(guān)的API(如ReentrantLcok或synchronized)進(jìn)行互斥控制。

但是在「分布式系統(tǒng)」中,由于分布式系統(tǒng)「多線程」、「多進(jìn)程」并且「分布在不同機(jī)器」上,這將使原單機(jī)并發(fā)控制鎖策略失效,為了解決這個問題就需要一種「跨JVM的互斥機(jī)制」來控制共享資源的訪問,這就得靠分布式鎖啦。

看透鎖本質(zhì)

在我看來:所有的鎖本身都可以用一個變量來表示。

比如:在「單機(jī)上運行」的多線程程序來說。取一個變量,變量為0時,表示沒有線程獲取鎖;變量為1時,表示已經(jīng)有線程獲取鎖。

加鎖:線程調(diào)用加鎖操作,檢查變量是否為0,如果為0,表示沒線程獲取鎖,將變量設(shè)置為1,表示獲取鎖;如果不是0,表示其他線程已經(jīng)暫用鎖,獲取鎖失敗。

解鎖:同理。

而分布式環(huán)境下,同樣可以以變量形式理解分布式鎖。

但是,和線程在單機(jī)上操作鎖不同的是,在分布式場景下,「鎖變量需要由一個共享存儲系統(tǒng)來維護(hù)」,只有這樣,多個客戶端才可以通過訪問共享存儲系統(tǒng)來訪問鎖變量。相應(yīng)的,「加鎖和釋放鎖的操作就變成了讀取、判斷和設(shè)置共享存儲系統(tǒng)中的鎖變量值」。

「可見,滿足分布式鎖的要求」:

  • 「鎖操作原子性」:分布式鎖的加鎖和釋放鎖的過程,涉及多個操作。所以,在實現(xiàn)分布式鎖時,我們需要保證這些「鎖操作的原子性」;
  • 「鎖的可靠性」:共享存儲系統(tǒng)保存了鎖變量,如果共享存儲系統(tǒng)發(fā)生故障或宕機(jī),那么客戶端也就無法進(jìn)行鎖操作了。在實現(xiàn)分布式鎖時,我們需要考慮保證「共享存儲系統(tǒng)的可靠性」,進(jìn)而保證「鎖的可靠性」。

上面我們提到了可以使用一個鎖變量來表示鎖,其實你也可以理解為「占位」。只不過分布式鎖需要把這個坑位拿出來放于「共享」的地方,每個都從「共享處來檢查坑位」。

占位一般是使用 setnx(set if not exists) 指令,只允許被一個客戶端占位。先來先占, 用完了,再調(diào)用 del 指令釋放茅坑。

  1. //加鎖 
  2. > setnx lock_key 1 
  3. OK 
  4. //業(yè)務(wù)邏輯 
  5. >(其他操作) 
  6. //釋放鎖 
  7. > del lock_key 

但是有個問題,如果邏輯執(zhí)行到中間出現(xiàn)異常了,可能會導(dǎo)致 del 指令沒有被調(diào)用,這樣就會「陷入死鎖」,鎖永遠(yuǎn)得不到釋放。

于是我們在拿到鎖之后,再給鎖加上一個過期時間,這樣即使中間出現(xiàn)異常也可以保證指定時間之后鎖會自動釋放。

  1. //加鎖 
  2. > setnx lock_key 1 
  3. OK 
  4. > expire lock_key 5 
  5. //業(yè)務(wù)邏輯 
  6. >(其他操作) 
  7. //釋放鎖 
  8. > del lock_key 

但是以上邏輯還有問題。如果在 setnx 和 expire 之間服務(wù)器進(jìn)程突然掛掉了,可能是因為機(jī)器掉電或者是被人為殺掉的,就會導(dǎo)致 expire 得不到執(zhí)行,也會造成死鎖。

這種問題的根源就在于 setnx 和 expire 是兩條指令而不是原子指令。你也許會想到使用事務(wù)什么的執(zhí)行,但是這里不行,因為如果 setnx 沒搶到鎖,expire 是不應(yīng)該執(zhí)行的。

Redis 2.8 版本中作者加入了 set 指令的擴(kuò)展參數(shù),使得 setnx 和expire 指令可以一起執(zhí)行,徹底解決了分布式鎖的亂象。

  1. set key value [EX seconds | PX milliseconds] [NX] 

除了上述基本常規(guī)的問題,還有這些「你可能沒考慮到的問題」:

超時問題

Redis 的分布式鎖不能解決超時問題,如果在加鎖和釋放鎖間的業(yè)務(wù)邏輯執(zhí)行時間太長,以至于超出了鎖的超時限制,就會出現(xiàn)問題(也就是鎖過期了,你的業(yè)務(wù)邏輯還沒執(zhí)行完)。

因為這時候鎖過期了,第二個客戶端B重新持有了這把鎖,但是緊接著客戶端A執(zhí)行完了業(yè)務(wù)邏輯,就把鎖給釋放了,客戶端C就會在客戶端B邏輯執(zhí)行完之間拿到了鎖。為了避免這個問題,Redis 分布式鎖不要用于較長時間的任務(wù)。

為了應(yīng)對這個問題,我們需要能區(qū)分來自不同客戶端的鎖操作,具體咋做呢 ? 針對于這個問題,我們可以想辦法把命令略加點小技巧。可以在鎖變量的值上想想辦法。

在使用SETNX命令進(jìn)行加鎖的方法中,我們通過把鎖變量值設(shè)置為1或0,表示是否加鎖成功。1和0只有兩種狀態(tài),無法表示究竟是哪個客戶端進(jìn)行的鎖操作。

所以,我們在加鎖操作時,可以「讓每個客戶端給鎖變量設(shè)置一個唯一值」,這里的唯一值就可以用來標(biāo)識當(dāng)前操作的客戶端。

在釋放鎖操作時,客戶端需要判斷,當(dāng)前「鎖變量的值是否和自己的唯一標(biāo)識相等」,只有在相等的情況下,才能釋放鎖。這樣一來,就不會出現(xiàn)誤釋放鎖的問題了。

于是,我們的命令可以這樣寫:

  1. //加鎖,unique_value作為客戶端唯—性的標(biāo)識 
  2. SET lock_key unique_value NX PX 5000 

其中,unique_value 是客戶端的唯一標(biāo)識,可以用一個隨機(jī)生成的字符串來表示,PX 5000則表示 lock_key會在5s后過期,以免客戶端在這期間發(fā)生異常而無法釋放鎖。

因為在加鎖操作中,每個客戶端都使用了一個唯一標(biāo)識,所以在「釋放鎖操作」時,我們需要「判斷鎖變量的值」,是否等于執(zhí)行釋放鎖操作的客戶端的唯一標(biāo)識,如下所示,可以使用Lua腳本來保證原子性:

  1. //釋放鎖比較unique_value是否相等,避免誤釋放 
  2. if redis.call("get" ,KEYS[1])== ARGV[1] then 
  3.  return redis.call("del" , KEYS[1]) 
  4. else 
  5.  return 0 
  6. end 

可重入性

可重入性是指線程在持有鎖的情況下再次請求加鎖,如果一個鎖支持同一個線程的多次加鎖,那么這個鎖就是可重入的。比如 Java 語言里有個 ReentrantLock 就是可重入鎖。

Redis 分布式鎖如果要支持可重入,可以對客戶端的 set 方法進(jìn)行包裝,使用線程的 Threadlocal 變量存儲當(dāng)前持有鎖的計數(shù)。

此處就不過多介紹,大抵不會問,有興趣可以自己上網(wǎng)查閱看書。

課外補(bǔ)充

上述內(nèi)容,是個基于單個Redis節(jié)點實現(xiàn)分布式鎖。

當(dāng)我們要實現(xiàn)「高可靠的分布式鎖」時,就不能只依賴單個的命令操作了,我們需要按照一定的步驟和規(guī)則進(jìn)行加解鎖操作,否則,就可能會出現(xiàn)鎖無法工作的情況。“一定的步驟和規(guī)則”是指啥呢?其實就是分布式鎖的算法。

這里簡單介紹Redlock算法的執(zhí)行步驟。Redlock算法的實現(xiàn)需要有N個獨立的Redis實例。接下來,我們可以分成3步來完成加鎖操作。

1、客戶端獲取當(dāng)前時間

2、客戶端按照順序在每個Master實例中嘗試獲得鎖。在獲得鎖的過程中,為每一個鎖操作設(shè)置一個快速失敗時間(如果想要獲得一個10秒的鎖,那么每一個鎖操作的失敗時間設(shè)為5-50ms)。

這樣可以避免客戶端與一個已經(jīng)故障的Master通信占用太長時間,通過快速失敗的方式盡快的與集群中的其他節(jié)點完成鎖操作。

3、客戶端計算出與master獲得鎖操作過程中消耗的時間,「當(dāng)且僅當(dāng)Client獲得鎖消耗的時間小于鎖的存活時間,并且在一半以上的master節(jié)點中獲得鎖」。才認(rèn)為client成功的獲得了鎖。

4、如果已經(jīng)獲得了鎖,「Client執(zhí)行任務(wù)的時間窗口是鎖的存活時間減去獲得鎖消耗的時間。」

 

5、如果Client獲得鎖的數(shù)量不足一半以上,或獲得鎖的時間超時,那么認(rèn)為獲得鎖失敗。客戶端「需要嘗試在所有的master節(jié)點中釋放鎖, 即使在第二步中沒有成功獲得該Master節(jié)點中的鎖,仍要進(jìn)行釋放操作。」

 

責(zé)任編輯:武曉燕 來源: 小龍coding
相關(guān)推薦

2019-06-19 15:40:06

分布式鎖RedisJava

2022-01-06 10:58:07

Redis數(shù)據(jù)分布式鎖

2023-08-21 19:10:34

Redis分布式

2019-02-26 09:51:52

分布式鎖RedisZookeeper

2022-09-19 08:17:09

Redis分布式

2020-11-16 12:55:41

Redis分布式鎖Zookeeper

2024-10-07 10:07:31

2019-07-16 09:22:10

RedisZookeeper分布式鎖

2021-06-16 07:56:21

Redis分布式

2022-06-16 08:01:24

redis分布式鎖

2024-04-01 05:10:00

Redis數(shù)據(jù)庫分布式鎖

2023-01-13 07:39:07

2020-07-30 09:35:09

Redis分布式鎖數(shù)據(jù)庫

2021-07-26 11:09:46

Redis分布式技術(shù)

2020-07-15 16:50:57

Spring BootRedisJava

2021-03-10 09:54:06

Redis分布式

2023-03-01 08:07:51

2022-07-22 06:55:20

Redis分布式鎖

2022-11-14 07:23:32

RedisJedis分布式鎖

2022-03-08 15:24:23

BitMapRedis數(shù)據(jù)
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 欧美a级成人淫片免费看 | 欧美日韩精品一区二区 | 国产露脸国语对白在线 | 婷婷精品 | 精品国产精品三级精品av网址 | 一区二区三区中文字幕 | 国产精品久久久久一区二区三区 | 久久精品国产一区二区电影 | 99免费在线观看视频 | 色视频网站免费 | 亚洲一区二区黄 | 四虎首页| 91久久久久 | 视频一区 国产精品 | 亚洲一页 | 亚洲视频免费一区 | 国产三级| 成年免费视频 | 成人精品在线视频 | 中文字幕精品一区二区三区精品 | 大香在线伊779 | 在线色网| 亚洲国产成人精品女人久久久 | 亚洲免费一区 | 在线免费观看黄色av | 国产精品久久久爽爽爽麻豆色哟哟 | 成年人网站免费 | 成人免费视频网站在线观看 | 99色在线视频 | 91婷婷韩国欧美一区二区 | 日本高清视频网站 | 国产日韩欧美91 | 亚洲一区二区三区视频 | 中文字幕三区 | 国产精品a久久久久 | 久久在线精品 | 久久综合久色欧美综合狠狠 | 丝袜一区二区三区 | 超碰导航 | 天天操综合网 | 色资源在线 |