借助於redis中的指令setnx(key, value),key不存在就新增,存在就什麼都不做。同時有多個客戶端發送setnx指令,只有一個客戶端可以成功,回傳1(true);其他的客戶端回傳0(false)。
本教學操作環境:windows7系統、Redis5.0.10版、DELL G3電腦。
分散式鎖定的實作
隨著業務發展的需要,原單體單機部署的系統被演化成分佈式叢集系統後,由於分散式系統多執行緒、多進程並且分佈在不同機器上,這將使原單機部署情況下的並發控制鎖策略失效,單純的Java API並不能提供分散式鎖定的能力。為了解決這個問題就需要一種跨JVM的互斥機制來控制共享資源的訪問,這就是分散式鎖要解決的問題!
分散式鎖定主流的實作方案:
基於資料庫實作分散式鎖定
基於快取(Redis等)
基於Zookeeper
這裡,我們就基於redis實作分散式鎖定。
基本實作
借助於redis中的指令setnx(key, value),key不存在就新增,存在就什麼都不做。同時有多個客戶端發送setnx指令,只有一個客戶端可以成功,回傳1(true);其他的客戶端回傳0(false)。
主要使用Redis Setnx 指令
在指定的key 不存在時,為key 設定指定的值
設定成功,返回1 。設定失敗,返回0
redis> EXISTS job # job 不存在 (integer) 0 redis> SETNX job "programmer" # job 设置成功 (integer) 1 redis> SETNX job "code-farmer" # 尝试覆盖 job ,失败 (integer) 0 redis> GET job # 没有被覆盖 "programmer"
java程式碼
public void testLock() { // 执行redis的setnx命令 String uuid = UUID.randomUUID().toString(); Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 5, TimeUnit.SECONDS); // 判断是否拿到锁 if (lock) { // 执行业务逻辑代码 // ... // 释放锁资源 (保证获取值和删除操作的原子性) LUA脚本保证删除的原子性 String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; this.redisTemplate.execute(new DefaultRedisScript<>(script), Arrays.asList("lock"), Arrays.asList(uuid)); // if (StrUtil.equals(uuid,redisTemplate.opsForValue().get("lock"))){ // redisTemplate.delete("lock"); // } } else { // 其他请求尝试获取锁 testLock(); } }
為了確保分散式鎖定可用,我們至少要確保鎖定的實作同時滿足以下四個條件:
互斥性。在任意時刻,只有一個客戶端能持有鎖。
不會發生死鎖。即使有一個客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保證後續其他客戶端能加鎖。
解鈴也須繫鈴人。加鎖和解鎖必須是同一個客戶端,客戶端自己不能把別人加的鎖給解了。
相關教學推薦:Redis教學
以上是redis分散式鎖實作原理是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!