什麼是分散式鎖?

choubou發表於2021-09-09

圖片描述

一、為什麼要使用分散式鎖?

為了保證一個方法或屬性在高併發情況下的同一時間只能被同一個執行緒執行,在傳統單體應用單機部署的情況下,可以使用併發處理相關的功能進行互斥控制。但是,隨著業務發展的需要,原單體單機部署的系統被演化成分散式叢集系統後,由於分散式系統多執行緒、多程式並且分佈在不同機器上,這將使原單機部署情況下的併發控制鎖策略失效,單純的應用並不能提供分散式鎖的能力。為了解決這個問題就需要一種跨機器的互斥機制來控制共享資源的訪問,這就是分散式鎖要解決的問題!

 

二、什麼是分散式鎖?

分散式鎖其實可以理解為:控制分散式系統有序的去對共享資源進行操作,透過互斥來保持一致性。 舉個不太恰當的例子:假設共享的資源就是一個房子,裡面有各種書,分散式系統就是要進屋看書的人,分散式鎖就是保證這個房子只有一個門並且一次只有一個人可以進,而且門只有一把鑰匙。然後許多人要去看書,可以,排隊,第一個人拿著鑰匙把門開啟進屋看書並且把門鎖上,然後第二個人沒有鑰匙,那就等著,等第一個出來,然後你在拿著鑰匙進去,然後就是以此類推

 

1.執行緒鎖

主要用來給方法、程式碼塊加鎖。當某個方法或程式碼使用鎖,在同一時刻僅有一個執行緒執行該方法或該程式碼段。執行緒鎖只在同一JVM中有效果,因為執行緒鎖的實現在根本上是依靠執行緒之間共享記憶體實現的,比如synchronized是共享物件頭,顯示鎖Lock是共享某個變數(state)。

2.程式鎖

為了控制同一作業系統中多個程式訪問某個共享資源,因為程式具有獨立性,各個程式無法訪問其他程式的資源,因此無法透過synchronized等執行緒鎖實現程式鎖。

3.分散式鎖

當多個程式不在同一個系統中,用分散式鎖控制多個程式對資源的訪問。


三、分散式鎖應該具備哪些條件?

分散式鎖應該具備以下條件:

1、在分散式系統環境下,一個方法在同一時間只能被一個機器的一個執行緒執行;
2、高可用的獲取鎖與釋放鎖;
3、高效能的獲取鎖與釋放鎖;
4、具備可重入特性;
5、具備鎖失效機制,防止死鎖;
6、具備非阻塞鎖特性,即沒有獲取到鎖將直接返回獲取鎖失敗。

 

為了確保分散式鎖可用,我們至少要確保鎖的實現同時滿足以下四個條件:

  1、互斥性:任意時刻,只能有一個客戶端獲取鎖,不能同時有兩個客戶端獲取到鎖。

  2、安全性:鎖只能被持有該鎖的客戶端刪除,不能由其它客戶端刪除。

  3、死鎖:獲取鎖的客戶端因為某些原因(如down機等)而未能釋放鎖,其它客戶端再也無法獲取到該鎖。

  4、容錯:當部分節點(redis節點等)down機時,客戶端仍然能夠獲取鎖和釋放鎖。

 

四、分散式鎖實現原理

(1)互斥性:保證同一時間只有一個客戶端可以拿到鎖,也就是可以對共享資源進行操作

(2)安全性:只有加鎖的服務才能有解鎖許可權,也就是不能讓a加的鎖,bcd都可以解鎖,如果都能解鎖那分散式鎖就沒啥意義了

可能出現的情況就是a去查詢發現持有鎖,就在準備解鎖,這時候忽然a持有的鎖過期了,然後b去獲得鎖,因為a鎖過期,b拿到鎖,這時候a繼續執行第二步進行解鎖如果不加校驗,就將b持有的鎖就給刪除了

(3)避免死鎖:出現死鎖就會導致後續的任何服務都拿不到鎖,不能再對共享資源進行任何操作了

(4)保證加鎖與解鎖操作是原子性操作

這個其實屬於是實現分散式鎖的問題,假設a用redis實現分散式鎖

假設加鎖操作,操作步驟分為兩步:

1,設定key set(key,value)2,給key設定過期時間

假設現在a剛實現set後,程式崩了就導致了沒給key設定過期時間就導致key一直存在就發生了死鎖

 

五、分散式鎖實現的核心要素是什麼?

1. 加鎖:要保證同一時間只有一個客戶端可以拿到鎖,得到鎖之後開始執行相關業務。

2. 解鎖:在業務執行完畢後,須及時釋放鎖,以便其他執行緒可以進入。

3. 鎖超時:如果一個得到鎖的執行緒在執行任務的過程中掛掉,來不及顯式地釋放鎖,這塊資源將會永遠被鎖住(死鎖),別的執行緒再也別想進來。所以需要設定一個超時時間,以保證即使沒有被顯式釋放,這把鎖也要在一定時間後自動釋放,避免死鎖。

 

六、分散式鎖的實現方式有哪些?

目前幾乎很多大型網站及應用都是分散式部署的,分散式場景中的資料一致性問題一直是一個比較重要的話題。分散式的CAP理論告訴我們“任何一個分散式系統都無法同時滿足一致性(Consistency)、可用性(Availability)和分割槽容錯性(Partition tolerance),最多隻能同時滿足兩項。”所以,很多系統在設計之初就要對這三者做出取捨。在網際網路領域的絕大多數的場景中,都需要犧牲強一致性來換取系統的高可用性,系統往往只需要保證“最終一致性”,只要這個最終時間是在使用者可以接受的範圍內即可。

在很多場景中,我們為了保證資料的最終一致性,需要很多的技術方案來支援,比如分散式事務、分散式鎖等。有的時候,我們需要保證一個方法在同一時間內只能被同一個執行緒執行。

常見的分散式鎖的實現方式有:

基於資料庫實現分散式鎖;

基於快取(Redis等)實現分散式鎖;

基於Zookeeper實現分散式鎖;

 

一、基於資料庫實現分散式鎖

基於資料庫表實現分散式鎖

基於資料庫的實現方式的核心思想是:在資料庫中建立一個表,表中包含方法名等欄位,並在方法名欄位上建立唯一索引,想要執行某個方法,就使用這個方法名向表中插入資料,成功插入則獲取鎖,執行完成後刪除對應的行資料釋放鎖。

具體的實現步驟如下:

(1)建立如下資料表

DROP TABLE IF EXISTS `method_lock`;
CREATE TABLE `method_lock` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `method_name` varchar(64) NOT NULL COMMENT '鎖定的方法名',
  `desc` varchar(255) NOT NULL COMMENT '備註資訊',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_method_name` (`method_name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='鎖定中的方法';


(2)想要執行某個方法,就呼叫這個方法向資料表method_lock中插入資料:

INSERT INTO method_lock (method_name, desc) VALUES ('methodName', '測試的methodName');

因為我們對method_name做了唯一性約束,這裡如果有多個請求同時提交到資料庫的話,資料庫會保證只有一個操作可以成功,那麼我們就可以認為操作成功的那個執行緒獲得了該方法的鎖,可以執行方法體內容。

(3)成功插入則表示獲取到鎖,插入失敗則表示獲取鎖失敗;插入成功後,就好繼續方法體的內容,執行完成後刪除對應的行資料釋放鎖:

delete from method_lock where method_name ='methodName';

存在的缺點:

●這把鎖強依賴資料庫的可用性,資料庫是一個單點,一旦資料庫掛掉,會導致業務系統不可用。

●這把鎖沒有失效時間,一旦解鎖操作失敗,就會導致鎖記錄一直在資料庫中,其他執行緒無法再獲得到鎖。

●這把鎖只能是非阻塞的,因為資料的insert操作,一旦插入失敗就會直接報錯。沒有獲得鎖的執行緒並不會進入排隊佇列,要想再次獲得鎖就要再次觸發獲得鎖操作。

●這把鎖是非重入的,同一個執行緒在沒有釋放鎖之前無法再次獲得該鎖。因為資料中資料已經存在了。

●這把鎖是非公平鎖,所有等待鎖的執行緒憑運氣去爭奪鎖。

以上這種方法只是採用資料庫表實現分散式鎖的一種方法,還有很多其他方法。

 

使用基於資料庫表實現分散式鎖的一些問題及最佳化:

1、因為是基於資料庫實現的,資料庫的可用性和效能將直接影響分散式鎖的可用性及效能,所以,資料庫需要雙機部署、資料同步、主備切換;

2、不具備可重入的特性,因為同一個執行緒在釋放鎖之前,行資料一直存在,無法再次成功插入資料,所以,需要在表中新增一列,用於記錄當前獲取到鎖的機器和執行緒資訊,在再次獲取鎖的時候,先查詢表中機器和執行緒資訊是否和當前機器和執行緒相同,若相同則直接獲取鎖;

3、沒有鎖失效機制,因為有可能出現成功插入資料後,伺服器當機了,對應的資料沒有被刪除,當服務恢復後一直獲取不到鎖,所以,需要在表中新增一列,用於記錄失效時間,並且需要有定時任務清除這些失效的資料;

4、不具備阻塞鎖特性,獲取不到鎖直接返回失敗,所以需要最佳化獲取邏輯,迴圈多次去獲取。

5、在實施的過程中會遇到各種不同的問題,為了解決這些問題,實現方式將會越來越複雜;依賴資料庫需要一定的資源開銷,效能問題需要考慮。

 

 

基於資料庫排它鎖實現分散式鎖

利用我們的建立method_lock表,透過資料庫的排他鎖來實現分散式鎖。 基於MySql的InnoDB引擎,可以使用以下方法來實現加鎖操作:

"select * from methodLock where method_name= '" + methodName + "' for update";  //悲觀鎖

虛擬碼:

public boolean lock(){
     connection.setAutoCommit(false)
     while(true){
         try{
             result = select * from methodLock where method_name=xxx for update;
             if(result==null){
                 return true;
             }
         }catch(Exception e){
 
         }
         sleep(1000);
     }
     return false;
 }

 

在查詢語句後面增加for update,資料庫會在查詢過程中給資料庫表增加排他鎖。當某條記錄被加上排他鎖之後,其他執行緒無法再在該行記錄上增加排他鎖。

我們可以認為獲得排它鎖的執行緒即可獲得分散式鎖,當獲取到鎖之後,可以執行方法的業務邏輯,執行完方法之後,再透過以下方法解鎖:

虛擬碼:

public void unlock(){
     connection.commit();
 }

透過connection.commit();操作來釋放鎖。

這種方法可以有效的解決上面提到的無法釋放鎖和阻塞鎖的問題。

阻塞鎖? for update語句會在執行成功後立即返回,在執行失敗時一直處於阻塞狀態,直到成功。

 

鎖定之後服務當機,無法釋放?使用這種方式,服務當機之後資料庫會自己把鎖釋放掉。

但是還是無法直接解決資料庫單點、可重入和公平鎖的問題。

總結一下使用資料庫來實現分散式鎖的方式,這兩種方式都是依賴資料庫的一張表,一種是透過表中的記錄的存在情況確定當前是否有鎖存在,另外一種是透過資料庫的排他鎖來實現分散式鎖。

資料庫實現分散式鎖的優點:直接藉助資料庫,容易理解。

資料庫實現分散式鎖的缺點:

會有各種各樣的問題,在解決問題的過程中會使整個方案變得越來越複雜。

運算元據庫需要一定的開銷,效能問題需要考慮。

 

二、基於快取實現分散式鎖

1、選用Redis實現分散式鎖原因:

(1)Redis有很高的效能;
(2)Redis命令對此支援較好,實現起來比較方便

 

2使用命令介紹:

(1)加鎖:
SETNX key value:當且僅當key不存在時,set一個key為value的字串,返回1;若key存在,則什麼都不做,返回0。

(2)設定鎖的超時:
expire key timeout:為key設定一個超時時間,單位為second,超過這個時間鎖會自動釋放,避免死鎖。

(3)釋放鎖:
del key:刪除key

在使用Redis實現分散式鎖的時候,主要就會使用到這三個命令。

除此之外,我們還可以使用set key value NX EX max-lock-time 實現加鎖,並且使用 EVAL 命令執行lua指令碼實現解鎖。

EX seconds : 將鍵的過期時間設定為 seconds 秒。 執行 SET key value EX seconds 的效果等同於執行 SETEX key seconds value 。

 

PX milliseconds : 將鍵的過期時間設定為 milliseconds 毫秒。 執行 SET key value PX milliseconds 的效果等同於執行 PSETEX key milliseconds value 。

 

NX : 只在鍵不存在時, 才對鍵進行設定操作。 執行 SET key value NX 的效果等同於執行 SETNX key value 。

 

XX : 只在鍵已經存在時, 才對鍵進行設定操作。

 

3、實現思想:

(1)獲取鎖的時候,使用setnx加鎖,並使用expire命令為鎖新增一個超時時間,超過該時間則自動釋放鎖,鎖的value值為一個隨機生成的UUID,透過此在釋放鎖的時候進行判斷。

(2)獲取鎖的時候還設定一個獲取的超時時間,若超過這個時間則放棄獲取鎖。

(3)釋放鎖的時候,透過UUID判斷是不是該鎖,若是該鎖,則執行delete進行鎖釋放。

例項程式碼:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

import java.util.Collections;

/**
 * @ClassName: RedisTool2
 * @Author: liuhefei
 * @Description: Redis實現分散式鎖
 * @Date: 2020/6/9 17:57
 */
public class RedisTool2 {

    private static Jedis jedis = new Jedis("127.0.0.1",6379);

    private static final String LOCK_SUCCESS = "OK";

    private static final String SET_IF_NOT_EXIST = "NX";

    private static final String SET_WITH_EXPIRE_TIME = "PX";

    private static final Long RELEASE_SUCCESS = 1L;


    /**
     * EX seconds : 將鍵的過期時間設定為 seconds 秒。 執行 SET key value EX seconds 的效果等同於執行 SETEX key seconds value 。
     *
     * PX milliseconds : 將鍵的過期時間設定為 milliseconds 毫秒。 執行 SET key value PX milliseconds 的效果等同於執行 PSETEX key milliseconds value 。
     *
     * NX : 只在鍵不存在時, 才對鍵進行設定操作。 執行 SET key value NX 的效果等同於執行 SETNX key value 。
     *
     * XX : 只在鍵已經存在時, 才對鍵進行設定操作。
     */

    /**
   * 嘗試獲取分散式鎖
   * @param lockKey 鎖
   * @param requestId 請求標識
   * @param expireTime 超期時間(過期時間) 需要根據實際的業務場景確定
   * @return 是否獲取成功
   */
    public static boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
        SetParams params = new SetParams();
        String result = jedis.set(lockKey, requestId, params.nx().ex(expireTime));
        if (LOCK_SUCCESS.equals(result)) {
             return true;
        }
        return false;
    }


    /**
     * 嘗試獲取分散式鎖
     * @param lockKey 鎖
     * @param requestId 請求標識
     * @param expireTime 超期時間(過期時間)需要根據實際的業務場景確定
     * @return 是否獲取成功
     */
    public static boolean tryGetDistributedLock1(String lockKey, String requestId, int expireTime){
        //只在鍵 key 不存在的情況下, 將鍵 key 的值設定為 value 。若鍵 key 已經存在, 則 SETNX 命令不做任何動作。設定成功返回1,失敗返回0
        long code = jedis.setnx(lockKey, requestId);   //保證加鎖的原子操作
        //透過timeOut設定過期時間保證不會出現死鎖【避免死鎖】
        jedis.expire(lockKey, expireTime);   //設定鍵的過期時間
        if(code == 1){
            return true;
        }
        return false;
    }

    /**
     * 解鎖操作
     * @param key 鎖標識
     * @param value 客戶端標識
     * @return
     */

    public static Boolean unLock(String key,String value){

        //luaScript 這個字串是個lua指令碼,代表的意思是如果根據key拿到的value跟傳入的value相同就執行del,否則就返回0【保證安全性】
        String luaScript = "if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else  return 0 end";

        //jedis.eval(String,list,list);這個命令就是去執行lua指令碼,KEYS的集合就是第二個引數,ARGV的集合就是第三引數【保證解鎖的原子操作】
        Object var2 = jedis.eval(luaScript, Collections.singletonList(key), Collections.singletonList(value));

        if (RELEASE_SUCCESS == var2) {
            return true;
        }
        return false;
    }

    /**
     * 解鎖操作
     * @param key  鎖標識
     * @param value  客戶端標識
     * @return
     */
    public static Boolean unLock1(String key, String value){
        //key就是redis的key值作為鎖的標識,value在這裡作為客戶端的標識,只有key-value都比配才有刪除鎖的權利【保證安全性】
        String oldValue = jedis.get(key);
        long delCount = 0;  //被刪除的key的數量
        if(oldValue.equals(value)){
            delCount = jedis.del(key);
        }
        if(delCount > 0){  //被刪除的key的數量大於0,表示刪除成功
            return true;
        }
        return false;
    }


    /**
     * 重試機制:
     * 如果在業務中去拿鎖如果沒有拿到是應該阻塞著一直等待還是直接返回,這個問題其實可以寫一個重試機制,
     * 根據重試次數和重試時間做一個迴圈去拿鎖,當然這個重試的次數和時間設多少合適,是需要根據自身業務去衡量的
     * @param key 鎖標識
     * @param value 客戶端標識
     * @param timeOut 過期時間
     * @param retry 重試次數
     * @param sleepTime 重試間隔時間
     * @return
     */
    public Boolean lockRetry(String key,String value,int timeOut,Integer retry,Long sleepTime){
        Boolean flag = false;
        try {
            for (int i=0;i<retry;i++){
                flag = tryGetDistributedLock(key,value,timeOut);
                if(flag){
                    break;
                }
                Thread.sleep(sleepTime);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return flag;
    }

}

 

可能存在的問題及最佳化:

1、單點問題。解決辦法:採用多臺redis叢集部署。

2、這把鎖沒有失效時間,一旦解鎖操作失敗,就會導致鎖記錄一直在redis中,其他執行緒無法再獲得到鎖。解決辦法:redis的setExpire方法支援傳入失效時間,到達時間之後資料會自動刪除。

3、這把鎖只能是非阻塞的,無論成功還是失敗都直接返回。解決辦法:採用while重複執行。

4、這把鎖是非重入的,一個執行緒獲得鎖之後,在釋放鎖之前,無法再次獲得該鎖,因為使用到的key在redis中已經存在。無法再執行setNX操作。解決辦法:在一個執行緒獲取到鎖之後,把當前主機資訊和執行緒資訊儲存起來,下次再獲取之前先檢查自己是不是當前鎖的擁有者。

5、這把鎖是非公平的,所有等待的執行緒同時去發起setNX操作,運氣好的執行緒能獲取鎖。解決辦法:線上程獲取鎖之前先把所有等待的執行緒放入一個佇列中,然後按先進先出原則獲取鎖。


三、基於zookeeper實現分散式鎖

基於zookeeper臨時有序節點可以實現的分散式鎖

ZooKeeper是一個為分散式應用提供一致性服務的開源元件,它內部是一個分層的檔案系統目錄樹結構,規定同一個目錄下只能有一個唯一檔名。

 

Zookeeper的資料儲存結構就像一棵樹,這棵樹由節點組成,這種節點叫做Znode。

Znode分為四種型別:

1.持久節點 (PERSISTENT)

預設的節點型別。建立節點的客戶端與zookeeper斷開連線後,該節點依舊存在 。

 

2.持久節點順序節點(PERSISTENT_SEQUENTIAL)

所謂順序節點,就是在建立節點時,Zookeeper根據建立的時間順序給該節點名稱進行編號:

 

3.臨時節點(EPHEMERAL)

和持久節點相反,當建立節點的客戶端與zookeeper斷開連線後,臨時節點會被刪除:

 

4.臨時順序節點(EPHEMERAL_SEQUENTIAL)

顧名思義,臨時順序節點結合和臨時節點和順序節點的特點:在建立節點時,Zookeeper根據建立的時間順序給該節點名稱進行編號;當建立節點的客戶端與zookeeper斷開連線後,臨時節點會被刪除。

 

基於ZooKeeper實現分散式鎖的步驟如下:
(1)建立一個目錄mylock;
(2)執行緒A想獲取鎖就在mylock目錄下建立臨時順序節點;
(3)獲取mylock目錄下所有的子節點,然後獲取比自己小的兄弟節點,如果不存在,則說明當前執行緒順序號最小,獲得鎖;
(4)執行緒B獲取所有節點,判斷自己不是最小節點,設定監聽比自己次小的節點;
(5)執行緒A處理完,刪除自己的節點,執行緒B監聽到變更事件,判斷自己是不是最小的節點,如果是則獲得鎖。

原文連結:https://www.cnblogs.com/liuqingzheng/p/11080501.html

 

大致思想即為:每個客戶端對某個方法加鎖時,在zookeeper上的與該方法對應的指定節點的目錄下,生成一個唯一的瞬時有序節點。 判斷是否獲取鎖的方式很簡單,只需要判斷有序節點中序號最小的一個。 當釋放鎖的時候,只需將這個瞬時節點刪除即可。同時,其可以避免服務當機導致的鎖無法釋放,而產生的死鎖問題。

原文連結:https://blog.csdn.net/u010963948/article/details/79006572

 

使用zookeeper能解決的分散式問題:

鎖無法釋放?使用Zookeeper可以有效的解決鎖無法釋放的問題,因為在建立鎖的時候,客戶端會在ZK中建立一個臨時節點,一旦客戶端獲取到鎖之後突然掛掉(Session連線斷開),那麼這個臨時節點就會自動刪除掉。其他客戶端就可以再次獲得鎖。

 

非阻塞鎖?使用Zookeeper可以實現阻塞的鎖,客戶端可以透過在ZK中建立順序節點,並且在節點上繫結監聽器,一旦節點有變化,Zookeeper會通知客戶端,客戶端可以檢查自己建立的節點是不是當前所有節點中序號最小的,如果是,那麼自己就獲取到鎖,便可以執行業務邏輯了。

 

不可重入?使用Zookeeper也可以有效的解決不可重入的問題,客戶端在建立節點的時候,把當前客戶端的主機資訊和執行緒資訊直接寫入到節點中,下次想要獲取鎖的時候和當前最小的節點中的資料比對一下就可以了。如果和自己的資訊一樣,那麼自己直接獲取到鎖,如果不一樣就再建立一個臨時的順序節點,參與排隊。

 

單點問題?使用Zookeeper可以有效的解決單點問題,ZK是叢集部署的,只要叢集中有半數以上的機器存活,就可以對外提供服務。

 

公平問題?使用Zookeeper可以解決公平鎖問題,客戶端在ZK中建立的臨時節點是有序的,每次鎖被釋放時,ZK可以通知最小節點來獲取鎖,保證了公平。

 

Zookeeper資料同步問題:

Zookeeper是一個保證了弱一致性即最終一致性的分散式元件。

 

Zookeeper採用稱為Quorum Based Protocol的資料同步協議。假如Zookeeper叢集有N臺Zookeeper伺服器(N通常取奇數,3臺能夠滿足資料可靠性同時有很高讀寫效能,5臺在資料可靠性和讀寫效能方面平衡最好),那麼使用者的一個寫操作,首先同步到N/2 + 1臺伺服器上,然後返回給使用者,提示使用者寫成功。基於Quorum Based Protocol的資料同步協議決定了Zookeeper能夠支援什麼強度的一致性。

 

在分散式環境下,滿足強一致性的資料儲存基本不存在,它要求在更新一個節點的資料,需要同步更新所有的節點。這種同步策略出現在主從同步複製的資料庫中。但是這種同步策略,對寫效能的影響太大而很少見於實踐。因為Zookeeper是同步寫N/2+1個節點,還有N/2個節點沒有同步更新,所以Zookeeper不是強一致性的。

 

使用者的資料更新操作,不保證後續的讀操作能夠讀到更新後的值,但是最終會呈現一致性。犧牲一致性,並不是完全不管資料的一致性,否則資料是混亂的,那麼系統可用性再高分散式再好也沒有了價值。犧牲一致性,只是不再要求關係型資料庫中的強一致性,而是隻要系統能達到最終一致性即可。

 

推薦一個Apache的開源庫Curator(https://github.com/apache/curator/),它是一個ZooKeeper客戶端,Curator提供的InterProcessMutex是分散式鎖的實現,acquire方法用於獲取鎖,release方法用於釋放鎖。

Zookeeper實現分散式的優缺點:
優點:具備高可用、可重入、阻塞鎖特性,可解決失效死鎖問題。

缺點:因為需要頻繁的建立和刪除節點,效能上不如Redis方式。

 

 三種方式的對比:

從理解的難易程度角度(從低到高)

資料庫 > 快取 > Zookeeper

 

從實現的複雜性角度(從低到高)

Zookeeper >= 快取 > 資料庫

 

從效能角度(從高到低)

快取 > Zookeeper >= 資料庫

 

從可靠性角度(從高到低)

Zookeeper > 快取 > 資料庫

 

參考:https://blog.csdn.net/wuzhiwei549/article/details/80692278

https://blog.csdn.net/qq_40722827/article/details/102993655

https://blog.csdn.net/u010963948/article/details/79006572

https://www.cnblogs.com/liuqingzheng/p/11080501.html


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4548/viewspace-2825652/,如需轉載,請註明出處,否則將追究法律責任。

相關文章