首先你得知道什麼是分佈式鎖吧?,然後你得知道在什麼場景下需要去用到分佈式鎖吧?,然後你應該知道如何在你的業務中去利用Redis的分佈式鎖吧?,最後你得知道Redis的分佈式鎖的實現方式和其他的Zookeeper或則數據庫實現分佈式鎖有什麼區別吧?,然後他們之間的優缺點和好處吧?
然後本文主要是圍繞這幾個問題逐一帶你瞭解分佈式鎖的玩法,以及分佈式鎖的擴展知識點。
(1)什麼是分佈式鎖?
- 首先要想了解分佈式鎖,就必須得知道java中的lock鎖、synchronize鎖等,他們是用在當有多線程問題出現的時候(其實不就是併發嗎),爲了保證某個時間點只能有一個線程進來獲取到資源,所以java中的lock鎖這種他們是爲了解決多線程之間的資源獲取問題,並且他們都是運行在同一個JVM下的前提下。
- 但是分佈式鎖其實你可以理解爲如果有多個系統其實也就是同時啓用多個JVM(比如說在springcloud中,每次啓動一個服務就是啓動一個JVM也就是啓用一個進程),當多個JVM之間想要保證同一個時間點,只有一個JVM能夠去獲取對應的資源,其實分佈式鎖就是用來控制多進程之間的資源獲取的問題,所以衍生出了分佈式鎖的概念
(2)在什麼場景下需要去用到分佈式鎖?
- 如果部署在不同機器節點上的系統在同一條數據上面操作,比如多個節點機器對同一個訂單操作不同的流程有可能會導致該筆訂單最後狀態出現錯誤,造成損失。
- 使用分佈式鎖可以避免不同節點重複相同的工作,這些工作會浪費資源。比如用戶付了錢之後有可能不同節點會發出多封短信。
(3)如何在你的業務中去利用Redis的分佈式鎖?
- 一般都是會在自己的業務中定義一個自己的RedisService,把需要用到的API都封裝到這裏,然後我們只需要在我們的系統中用到的時候直接去調用就行了
- 使用 SET resource_name my_random_value NX PX 30000,這個命令是一個原子操作添加操作,my_random_value是由客戶端生成的一個隨機字符串,它要保證在足夠長的一段時間內在所有客戶端的所有獲取鎖的請求中都是唯一的,其中NX(if not exist)這個表示只有當resource_name對應的key值不存在的時候才能SET成功,然後PX 30000表示這個鎖有一個30秒的自動過期時間。當然,這裏30秒只是一個例子,客戶端可以選擇合適的過期時間。,如果當你要set成功的話,就會返回一個“OK”
- 其實那個隨機中的value值其實是爲了後面如果你想要去提前解鎖的話也是可以通過這個的值來實現的
- 完整的命令其實就是說:當前沒有鎖(key不存在),那麼就進行加鎖操作,並對鎖設置個有效期,同時value表示加鎖的客戶端。2. 已有鎖存在,不做任何操作。
public class RedisTool {
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”;
/**
* 嘗試獲取分佈式鎖
* @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, my_random_value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;}return false;
}
}
解鎖操作:
public class RedisTool {
private static final Long RELEASE_SUCCESS = 1L;
/**
* 釋放分佈式鎖
* @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;}
}
(4)這裏先簡單瞭解一下Zookeeper的是怎麼實現枷鎖的?
看了上面這個圖,其實已經是很形象了,Zookeeper其實就是把他一些資源就跟計算機中的文件存儲系統一樣,把每一個資源都當成一個文件夾,文件夾下又可以繼續放文件夾或者是文件(自己的電腦上首先文件夾不能重名吧,文件也不能重名吧,重名之後是不是也會有XXX副本(1),XXXX副本(2)的這種情況),然後你再把它想象爲是一顆二叉樹的形狀,然後他還有一個機制就是可以進行watch,你可以理解爲他可以時時刻刻在你要新建一個文件的時候是不是會重名
圖二;
這裏對上面的兩張圖來進行一個描述Zookeeper的加鎖過程和原理的流程:
- 首先你如果把一個lock文件夾下的resource_name1作爲你業務中需要用到的一個數據資源(可以比作是你的秒殺的一雙AJ吧,只有一雙限量的),那麼現在大家都來去搶這雙AJ
- 首先客戶端A發起請求過來想要去搶這個AJ,然後他就是會在resource_name1下又新建一個臨時文件xxxxxx_lock_0000001(這個名字在Zookeeper中叫做臨時順序節點),
(5)Redis的分佈式鎖的實現方式和其他的Zookeeper或則數據庫實現分佈式鎖有什麼區別?
- List item