以redisson爲例
RLock lock = redissonClient.getLock(key);
lock.lock(timeoutSecond, TimeUnit.SECONDS);
原則:1.自己加的鎖自己釋放,2.鎖到期了業務沒執行完還需續期
1. 加鎖時執行lua腳本
"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]);"
KEYS[1]
代表加鎖的key,ARGV[2]
代表的是加鎖的客戶端ID,ARGV[1]
代表的鎖的生存時間
2. 鎖互斥實現
如果其它客戶端來加鎖,第一個if會失敗,然後第二個if,然後再hash結構中判斷是否存在客戶端2的ID,因爲不含有,所以會走最後return返回過期時間,然後客戶端2會進入while循環,不停的嘗試加鎖。
3. watch dog自動延期機制
如果客戶端超過生存時間還想持有鎖,怎麼辦?
加鎖成功同時會啓動一個後臺線程檢查,如果客戶端還持有鎖就會不斷延長生存時間,默認是10s一次
4. 可重入
進入第二個if,會把value + 1
5. 釋放鎖
"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;"
先把鎖裏的計數減一,然後再刪除
7. redis分佈式鎖的缺點
master節點宕機,salve變成master,數據還沒來得及同步,這樣其他客戶端就有可能完成加鎖。就會導致多個客戶端同時持有了鎖。