天天看點

redis分布式鎖實作原理_redis分布式鎖實作原理

redis分布式鎖實作原理_redis分布式鎖實作原理

3.2. 分布式鎖的實作

随着業務發展的需要,原單體單機部署的系統被演化成分布式叢集系統後,由于分布式系統多線程、多程序并且分布在不同機器上,這将使原單機部署情況下的并發控制鎖政策失效,單純的Java API并不能提供分布式鎖的能力。為了解決這個問題就需要一種跨JVM的互斥機制來控制共享資源的通路,這就是分布式鎖要解決的問題!

分布式鎖主流的實作方案:

基于資料庫實作分布式鎖

基于緩存(Redis等)

基于Zookeeper

每一種分布式鎖解決方案都有各自的優缺點:

性能:redis最高

可靠性:zookeeper最高

這裡,我們就基于redis實作分布式鎖。

3.2.1. 基本實作

借助于redis中的指令setnx(key, value),key不存在就新增,存在就什麼都不做。同時有多個用戶端發送setnx指令,隻有一個用戶端可以成功,傳回1(true);其他的用戶端傳回0(false)。

redis分布式鎖實作原理_redis分布式鎖實作原理

主要使用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();

		}

	}
           

為了確定分布式鎖可用,我們至少要確定鎖的實作同時滿足以下四個條件:

互斥性。在任意時刻,隻有一個用戶端能持有鎖。

不會發生死鎖。即使有一個用戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保證後續其他用戶端能加鎖。

解鈴還須系鈴人。加鎖和解鎖必須是同一個用戶端,用戶端自己不能把别人加的鎖給解了。

加鎖和解鎖必須具有原子性。

繼續閱讀