主流分布式鎖實現(xiàn)方案

談到分布式系統(tǒng),就不得不談談分布式鎖沟蔑。而主流的實現(xiàn)java分布式鎖實現(xiàn)方案也就那么幾種,有基于Redis實現(xiàn)的,也有基于ZK的,有基于數(shù)據(jù)庫實現(xiàn)的分布式鎖,下面我將談談它們各種的實現(xiàn)方案撞叽。

基于Redis實現(xiàn)分布式鎖

基于Redis實現(xiàn)分布式鎖應該是比較普遍的,實現(xiàn)起來比較簡單.其主要是利用setnx來實現(xiàn)的,具體語法是setnx key val,當該key不存在時就設置value,如果已經(jīng)存在該key了就直接返回俏蛮。能這樣做主要得益于Redis的單線程結構,能保證setnx是原子性的,其偽代碼為:

 if (conn.setnx(lockKey, value) == 1) {

 }

這樣做存在一個問題:

  1. 沒有給lockKey設置過期時間,有可能導致該key一直不能釋放,從而使其它線程不能訪問撼泛。

有人立馬反應過來了,"這還不簡單?,用expire設置一下過期時間即可",所以就有了下面的這段代碼:

if (conn.setnx(lockKey, value) == 1) {
      conn.expire(lockKey, expireTime);//expireTime為過期時間
}

到底這樣做有沒有問題呢?考慮一下這種情況,當線程剛剛通過setnx設置值完畢后,系統(tǒng)因為某個原因宕機(斷點或者系統(tǒng)問題)導致還沒來得及執(zhí)行expire方法,這時也會引發(fā)和上面同樣的問題。也就是說簡單的設置一個過期時間還不行,因為沒法保證setnxexpire是原子操作,隨時都可能setnx成功但expire失敗吵冒。幸運的是,Redis提供了這樣一個原子性保證纯命。像Jedis沒有直接通過setnx設置過期時間的方法,可以使用這個方法:

 @Override
  public String set(final String key, final String value, final String nxxx, final String expx,
      final long time) {
    return new JedisClusterCommand<String>(connectionHandler, maxRedirections) {
      @Override
      public String execute(Jedis connection) {
        return connection.set(key, value, nxxx, expx, time);
      }
    }.run(key);
  }

????????還有一種情況,因為生產(chǎn)環(huán)境上Redis一般都是采用的集群,當master節(jié)點掛了以后會自動切換到slave。當其中一個線程拿到鎖后,此時Redis 的master節(jié)點宕機了,因為Redis是通過異步復制將數(shù)據(jù)同步到從節(jié)點的,鎖信息完全有可能沒有同步成功到從節(jié)點,其它線程就能通過setnx獲取到鎖,從而引發(fā)多個線程同時獲取鎖的問題痹栖。那該怎么解決呢亿汞?如果業(yè)務上可以接受,我覺得沒必要考慮這種情況,因為這種情況出現(xiàn)的幾率非常少,反之就必須想一個解決方案。
如果Redis是單主單從的話,這個問題基于Redis很難解決,如果是多主多從,可以考慮對所有的master都加鎖,即使其中一個master獲取鎖失敗,也不會影響其它兩個節(jié)點,當總數(shù)超過一半時也讓其獲取到鎖结耀。因為涉及到跨多個節(jié)點,需要小心的控制超時時間和鎖的釋放問題留夜。

最后,在方法結束時和拋出異常的時候需要手動釋放鎖,最好是在finally執(zhí)行。

基于Zookeeper實現(xiàn)分布式鎖

一般用Zookeeper實現(xiàn)分布式鎖有兩種方案:

  • 基于Zookeeper不能重復創(chuàng)建同一個節(jié)點
    利用名稱唯一性图甜,加鎖操作時碍粥,只需要所有客戶端一起創(chuàng)建/Lock/test節(jié)點,只有一個創(chuàng)建成功黑毅,成功者獲得鎖嚼摩。解鎖時,只需刪除/Lock/test節(jié)點,其余客戶端再次進入競爭創(chuàng)建節(jié)點枕面,直到所有客戶端都獲得鎖.
    這種方案的正確性和可靠性是ZooKeeper機制保證的愿卒,實現(xiàn)簡單。缺點是會產(chǎn)生“驚群”效應潮秘,假如許多客戶端在等待一把鎖琼开,當鎖釋放時候所有客戶端都被喚醒,僅僅有一個客戶端得到鎖枕荞。
  • 基于臨時有序節(jié)點
    對于加鎖操作柜候,可以讓所有客戶端都去/Lock目錄下創(chuàng)建臨時順序節(jié)點,如果創(chuàng)建的客戶端發(fā)現(xiàn)自身創(chuàng)建節(jié)點序列號是/Lock/目錄下最小的節(jié)點躏精,則獲得鎖渣刷。否則,監(jiān)視比自己創(chuàng)建節(jié)點的序列號小的節(jié)點(比自己創(chuàng)建的節(jié)點小的最大節(jié)點).進入等待矗烛。對于解鎖操作辅柴,只需要將自身創(chuàng)建的節(jié)點刪除即可,然后喚醒自己的后一個節(jié)點。
    特點:利用臨時順序節(jié)點來實現(xiàn)分布式鎖機制其實就是一種按照創(chuàng)建順序排隊的實現(xiàn)瞭吃。這種方案效率高碌嘀,避免了“驚群”效應,多個客戶端共同等待鎖虱而,當鎖釋放時只有一個客戶端會被喚醒筏餐。

由于Curator客戶端已經(jīng)提供了分布式鎖的實現(xiàn),可以直接使用如下代碼:

InterProcessMutex lock = new InterProcessMutex(client, lockPath);
if ( lock.acquire(maxWait, waitUnit) ) {
    try 
    {
        
    } finally {
        lock.release();
    }
}
基于數(shù)據(jù)庫實現(xiàn)分布式鎖

利用DB來實現(xiàn)分布式鎖,有兩種方案牡拇。兩種方案各有好壞,但是總體效果都不是很好穆律。但是實現(xiàn)還是比較簡單的惠呼。

  1. 利用主鍵唯一約束:
    我們知道數(shù)據(jù)庫是有唯一主鍵規(guī)則的,主鍵不能重復峦耘,對于重復的主鍵會拋出主鍵沖突異常剔蹋。
    其實這和分布式鎖實現(xiàn)方案基本是一致的,首先我們利用主鍵唯一規(guī)則辅髓,在爭搶鎖的時候向DB中寫一條記錄泣崩,這條記錄主要包含鎖的id、當前占用鎖的線程名洛口、重入的次數(shù)和創(chuàng)建時間等矫付,如果插入成功表示當前線程獲取到了鎖,如果插入失敗那么證明鎖被其他人占用第焰,等待一會兒繼續(xù)爭搶买优,直到爭搶到或者超時為止
  2. 利用Mysql行鎖的特性:
    利用for update加顯式的行鎖,這樣就能利用這個行級的排他鎖來實現(xiàn)分布式鎖了,同時unlock的時候只要釋放commit這個事務杀赢,就能達到釋放鎖的目的烘跺。
總結

Redis做分布式鎖實現(xiàn)起來比較簡單,如果不考慮master宕機引發(fā)的并發(fā)獲取鎖的問題,通過簡單的setnx就可以實現(xiàn),性能也很好。
如果項目中已經(jīng)使用了ZK也可以考慮使用使用zk來做分布式鎖,使用curator封裝好的分布式鎖即可,沒必要自己實現(xiàn)脂崔。但是zk的主要優(yōu)勢還是做分布式協(xié)調(diào),如果針對大壓力場景下可能會引發(fā)性能問題,最好將其它業(yè)務進行隔離滤淳。
數(shù)據(jù)庫做分布式鎖適合壓力比較小的情況,因為頻繁的for update可能造成數(shù)據(jù)庫連接的的耗盡,從而引發(fā)單點問題。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末砌左,一起剝皮案震驚了整個濱河市脖咐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绊困,老刑警劉巖文搂,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異秤朗,居然都是意外死亡煤蹭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門取视,熙熙樓的掌柜王于貴愁眉苦臉地迎上來硝皂,“玉大人,你說我怎么就攤上這事作谭』铮” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵折欠,是天一觀的道長贝或。 經(jīng)常有香客問我,道長锐秦,這世上最難降的妖魔是什么咪奖? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮酱床,結果婚禮上羊赵,老公的妹妹穿的比我還像新娘。我一直安慰自己扇谣,他們只是感情好昧捷,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著罐寨,像睡著了一般靡挥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上衩茸,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天芹血,我揣著相機與錄音贮泞,去河邊找鬼。 笑死幔烛,一個胖子當著我的面吹牛啃擦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播饿悬,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼令蛉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了狡恬?” 一聲冷哼從身側響起珠叔,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弟劲,沒想到半個月后祷安,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡兔乞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年汇鞭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庸追。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡霍骄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淡溯,到底是詐尸還是另有隱情读整,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布咱娶,位于F島的核電站米间,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏膘侮。R本人自食惡果不足惜车伞,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望喻喳。 院中可真熱鬧,春花似錦困曙、人聲如沸表伦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹦哼。三九已至,卻和暖如春要糊,著一層夾襖步出監(jiān)牢的瞬間纲熏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留局劲,地道東北人勺拣。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像鱼填,于是被迫代替她去往敵國和親药有。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355