使用redis實(shí)現(xiàn)的分布式鎖,真正想要分布式鎖還是用zk做好,redis的實(shí)現(xiàn)還是有風(fēng)險(xiǎn),簡(jiǎn)單用用還可以。
- redis集群中根據(jù)key進(jìn)行哈希分配到不同的hash槽,如果某個(gè)槽的機(jī)器down了,相當(dāng)于這些key都解鎖了。
- 如果程序執(zhí)行時(shí)間過長(zhǎng),鎖就被自動(dòng)釋放了。而且也不可能不加過期時(shí)間,否則剛加完鎖,機(jī)器down了,這個(gè)鎖就永遠(yuǎn)得不到釋放
- 使用zk的話,通過臨時(shí)節(jié)點(diǎn)加watcher可以完美避開redis分布式鎖的這些問題,就是加鎖解鎖寫起來比較復(fù)雜
- 使用redis實(shí)現(xiàn)的分布式鎖主要有兩點(diǎn)要注意:1.對(duì)redis的操作保持原子。2.保持誰加的鎖誰釋放。原理比較簡(jiǎn)單,不用細(xì)說了
private static final int LOCK_SECOND = 60;
private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
private static final String LOCK_SUCC = "OK";
private static final Long UNLOCK_SUCC = 1L;
public String lock(String key) {
if (StringUtils.isBlank(key)) {
return StringUtils.EMPTY;
}
key = StringAssist.joinUnderline(RedisKey.DISTRIBUTED_LOCK_PRE, key);
String uuid = UUID.randomUUID().toString();
try (Jedis jedis = pool.getResource()) {
String setRet = jedis.set(key, uuid, "NX", "EX", LOCK_SECOND);
if (StringUtils.equals(setRet, LOCK_SUCC)) { // 設(shè)置成功
return uuid;
}
// 設(shè)置失敗
return StringUtils.EMPTY;
} catch (Exception e) {
log.error("lock error, key:{}", key, e);
return StringUtils.EMPTY;
}
}
public boolean unlock(String key, String uuid) {
if (StringUtils.isAnyBlank(key, uuid)) {
return false;
}
key = StringAssist.joinUnderline(RedisKey.DISTRIBUTED_LOCK_PRE, key);
try (Jedis jedis = pool.getResource()) {
Object result = jedis.eval(UNLOCK_SCRIPT, Collections.singletonList(key), Collections.singletonList(uuid));
if (UNLOCK_SUCC.equals(result)) {
return true;
}
return false;
} catch (Exception e) {
log.error("unlock error, key:{}", key, e);
return false;
}
}
- 注意,這是redis官網(wǎng)的寫法~品質(zhì)有保證
