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