天天看點

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

繼續閱讀