天天看點

redis和zk分布式鎖使用場景

前言

本文介紹下分布式鎖的一個使用場景

分享本文的緣由是因為今天在寫代碼時需要處理一個原子性問題,場景是:業務功能需要先查詢資料,再根據資料判斷是否要更新資料,在這個查詢+更新的過程必然會存在高并發下的原子性問題

那麼如何解決這個問題呢,那麼就要說到我們的主角:分布式鎖了

分布式鎖介紹

分布式鎖:即在多叢集多節點環境下確定隻有一個線程可以拿到鎖,防止并發出現的問題,類似于synchronized,隻不過synchronized不能處理多節點的問題

解決上述問題的一種解決方式就是使用分布式鎖,雖然性能會比較低,但是筆者的場景是一個統計功能,并且是異步的,是以并不影響性能

核心代碼如下:

場景介紹

try {
	// 這裡可以根據業務場景做分段鎖,可以适當提升性能
	String lock = "lock_key";
	long keyValue = System.currentTimeMillis();
	// 擷取鎖,逾時時間60秒每隔0.5秒嘗試擷取一次,60秒後沒擷取到則放棄
	boolean getLock = redisUtils.getLockByInterval(lock,keyValue,60,0.5,0);
	if(getLock) {
		xxx
	}
} catch (Exception e) {
  log.error("異常:", e);
} finally {
    if(getLock) {
        redisUtils.unLock(lock,keyValue);
    }
}
           

RedisUtils類getLockByInterval

/**
     * 擷取鎖,如果沒擷取到那麼每隔interval秒重試一次,重試直到timeout秒
     * @param key
     * @param value
     * @param timeout 逾時時間(機關秒)
     * @param interval 逾時時間(機關秒)
     * @param currentTime 目前時間,調用時傳0,隻用于遞歸時間
     * @return
     */
    public boolean getLockByInterval(String key,Object value,int timeout,double interval,double currentTime) {
        if(currentTime > timeout) {
            return false;
        }
        // 相當于redis的setnx指令
        boolean getLock = redisTemplate.opsForValue().setIfAbsent(key,value,timeout, TimeUnit.SECONDS);
        if(!getLock) {
            //沒有拿到鎖就間隔interval再次嘗試擷取鎖,直到總時間大于timeout
            try {
                Thread.sleep(Double.valueOf(interval*1000).longValue());
            } catch (InterruptedException e) {
                log.error("RedisUtils getLockByInterval error:",e);
            }
            currentTime += interval;
            // 遞歸
            return getLockByInterval(key,value,timeout,interval,currentTime);
        }
        return true;
    }
           

上述代碼其實還是不夠完美,當并發量足夠大時可能存在某線程在逾時時間内還是沒有搶占到鎖,因為擷取鎖的機制是按照間隔時間來擷取的,并且屬于非公平鎖,即不是先到的線程有權利優先擷取鎖,這裡可以看到redis的分布式鎖并不是很友好,這裡再介紹下zookeeper的分布式鎖

分布式鎖對比

redis分布式鎖:通過redis通過的sexNx指令實作,即當key不存在時調用setNx傳回true,否則傳回false,擷取不到鎖的線程隻能輪詢去嘗試擷取鎖

優點:性能高,使用簡單,在允許偶發鎖失效的場景下推薦使用

缺點:通過輪詢搶占鎖的機制不是很可靠,當某線程占用鎖時間較長時可能導緻其他線程搶占鎖失敗

zookeeper分布式鎖:zk的分布式鎖機制是利用zk的臨時有序節點,即多個線程同時搶占鎖會建立多個節點如a1->a2->a3->a4->a5…,隻有拿到第一個節點的線程獲得鎖,裡面節點注冊是的watch機制,即a1使用完後會釋放目前節點,同時watch下一個節點a2,依次類推

優點:不依靠輪詢搶占鎖,依靠的是節點間的通信,比較可靠,當業務場景要求比較高是推薦使用

缺點:性能不如redis緩存鎖高