redis 分佈式鎖實現及存在的問題

1.什麼是分佈式鎖

爲了防止分佈式系統中的多個進程之間相互干擾,我們需要一種分佈式協調技術來對這些進程進行調度。而這個分佈式協調技術的核心就是來實現這個分佈式鎖

2.java中redis分佈式鎖的實現

    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;

    /**
     * 嘗試獲取分佈式鎖
     *
     * @param jedis      Redis客戶端
     * @param lockKey    鎖
     * @param requestId  請求標識
     * @param expireTime 超期時間
     * @return 是否獲取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {

        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

    /**
     * 釋放分佈式鎖 保證原子性
     * Lua代碼將被當成一個命令去執行,並且直到eval命令執行完成,Redis纔會執行其他命令
     *
     * @param jedis     Redis客戶端
     * @param lockKey   鎖
     * @param requestId 請求標識
     * @return 是否釋放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

3.還存在的問題

1)假設當前存在服務 A、B,當服務A獲取分佈式鎖後,redis還沒來的及去將鎖信息同步至其他redis服務器,這時候B去獲取鎖也會成功,如何解決這個問題呢?

現階段redis提供了基於集羣的 redlock 來解決這個問題,假設有5個redis節點,這些節點之間既沒有主從,也沒有集羣關係。客戶端用相同的key和隨機值在5個節點上請求鎖,請求鎖的超時時間應小於鎖自動釋放時間。當在3個(超過半數)redis上請求到鎖的時候,纔算是真正獲取到了鎖。如果沒有獲取到鎖,則把部分已鎖的redis釋放掉。

但這種方式並不適用於主從模式,主從模式下,master掛掉後,新選舉出來的master如果沒有同步到鎖,則B扔可以加鎖成功,這時候可以考慮,是否當鎖到過期時間後再選舉新的master,這樣就可以避免這種情況發生,但代價就是,在等待期間寫不可用,請慎重選擇。

2)假設當前存在服務A、B,當服務A獲取分佈式鎖後去執行,設置的過期時間爲30秒,但是A由於一些狀況,執行了30秒後仍未執行完畢,這時候B來獲取分佈式鎖,導致鎖失效。

這種情況下,redis提供了一個看門狗,及在獲取鎖的同時,我們啓動一個線程,定時去查看鎖的狀態,如果持有鎖,我們就對當前鎖延長有效時間,直道釋放鎖,當然這樣如果redis服務掛掉了也是存在問題的,所以一般情況下,我們會設置一個次數限制,或者最大延遲時間限制,以防止無線延遲下去。

最後說下,其實項目中我們可以直接使用Redisson框架,他的API中提供了大量的封裝好的功能,可以去如下地址進行查看學習:

https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章