簡單梳理Redis分佈式鎖 知識點

應用場景

當多個應用進程(客戶端)需要互斥地訪問共享資源時,可以使用分佈式鎖。其中Redis官方權威提出了RedLock。Java中可使用Redssion提供的實現。

注意點:

  1. 互斥,保證任何時刻只能有一個客戶端 獲取到鎖;
  2. 效率之死鎖,保證獲取到鎖的客戶端即使在出現網絡分區或者宕機的情況下,也能釋放掉鎖;
  3. 效率之容錯,保證只要大多數Redis節點正常工作,客戶端就能正常獲取到鎖和正常釋放鎖。

原理:
獲取鎖:

SET resource_name my_random_value NX [EX|PX] 30000

Redssion中的實現使用了Hash, 可重入:

if (redis.call(‘exists’, KEYS[1]) == 0)
then redis.call(‘hset’, KEYS[1], ARGV[2], 1);
redis.call(‘pexpire’, KEYS[1], ARGV[1]);
return nil;
end;
if (redis.call(‘hexists’, KEYS[1], ARGV[2]) == 1)
then redis.call(‘hincrby’, KEYS[1], ARGV[2], 1);
redis.call(‘pexpire’, KEYS[1], ARGV[1]);
return nil;
end;
return redis.call(‘pttl’, KEYS[1]);
Lua腳本解釋
如果鎖對應的Hash, key=resource_name的數據不存在,
那麼就設置:
hset resource_name my_random_value 1
pexpire resource_name 30000
如果鎖對應的Hash下, fieldKey=my_random_value存在,
那麼對其加1,即執行:
hincrby resource_name my_random_value 1
pexpire resource_name 30000
最後返回pttl resource_name

釋放鎖:

if redis.call(“get”,KEYS[1]) == ARGV[1] then
return redis.call(“del”,KEYS[1])
else
return 0
end

Redisson的釋放鎖實現:

if (redis.call(‘hexists’, KEYS[1], ARGV[3]) == 0)
then return nil;
end;
local counter = redis.call(‘hincrby’, KEYS[1], ARGV[3],
-1);
if (counter > 0)
then redis.call(‘pexpire’, KEYS[1], ARGV[2]);
return 0;
else redis.call(‘del’, KEYS[1]);
redis.call(‘publish’, KEYS[2], ARGV[1]);
return 1;
end;
return nil;

Redis分佈鎖常見的幾種實現方案:

  1. 單機
    缺點:單點故障,整個服務無法使用。

  2. 主從複製
    缺點:failover問題。 根本原因在於主從複製有延遲。當master在slave複製成功前宕機,slave晉升爲master時沒有宕機時的鎖信息,導致有可能出現兩個客戶端同時持有同一把鎖。
    舉例:
    a. client A 在master拿到鎖
    b. master節點把A創建的鎖信息同步到slave之前宕機了
    c. slave晉升爲master節點
    d. client B 在新master拿到與A相同的鎖(此時A以爲自己仍持有鎖)

…TODO

Redisson使用DEMO代碼片段:

使用Redisson加鎖 釋放鎖業務代碼
使用Redisson加鎖 釋放鎖業務代碼lock實例化RedissonClient

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章