Redis分布式锁 SET命令实现

xiaoxiao2021-02-28  24

参考:https://redis.io/commands/set

基于Redis命令:SET key valueNX EX max-lock-time  

适用于redis单机和redis集群模式

1.SET命令是原子性操作,NX指令保证只要当key不存在时才会设置value

2.设置的value要有唯一性,来确保锁不会被误删(value=系统时间戳+UUID)

3.上述命令执行返回OK时,客户端获取锁成功,否则失败

3.客户端可以通过redis释放脚本来释放锁

4.如果锁到达了最大生存时间将会自动释放

只有当前keyvalue和传入的value相同才会执行DEL命令

import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.StringRedisTemplate; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** * redis锁工具类 */ public class RedisLock { public static final String OK = "OK"; public static final String UNLOCK_LUA = "if redis.call(\"get\",KEYS[1]) == ARGV[1] " + "then " + " return redis.call(\"del\",KEYS[1]) " + "else " + " return 0 " + "end "; /** * 单机和集群redis分布式锁 * * 参考:https://redis.io/commands/set * * 版本:Redis Version >= 2.6.12 * * 命令:SET key-name uuid NX EX max-lock-time * * @param keyName redis key name * @param stringRedisTemplate stringRedisTemplate * @param expireSeconds 锁定的最大时长 * @return 锁定结果 */ public static LockRes tryLock(String keyName, StringRedisTemplate stringRedisTemplate, Integer expireSeconds) { // 将value设置为当前时间戳+随机数 String lockValue = System.currentTimeMillis() + UUID.randomUUID().toString(); String redisLockResult = stringRedisTemplate.execute((RedisCallback<String>) connection -> { Object nativeConnection = connection.getNativeConnection(); String result = null; // 集群 if (nativeConnection instanceof JedisCluster) { result = ((JedisCluster) nativeConnection).set(keyName, lockValue, "NX", "EX", expireSeconds); } // 单机 if (nativeConnection instanceof Jedis) { result = ((Jedis) nativeConnection).set(keyName, lockValue, "NX", "EX", expireSeconds); } return result; }); if (OK.equalsIgnoreCase(redisLockResult)) { return new LockRes(true, keyName, lockValue); } else { return new LockRes(false, keyName, null); } } public static Boolean unlock(LockRes lockRes, StringRedisTemplate stringRedisTemplate) { if (lockRes.isFlag()) { return stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> { Object nativeConnection = connection.getNativeConnection(); Long result = 0L; List<String> keys = new ArrayList<>(); keys.add(lockRes.getKey()); List<String> values = new ArrayList<>(); values.add(lockRes.getValue()); // 集群 if (nativeConnection instanceof JedisCluster) { result = (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, values); } // 单机 if (nativeConnection instanceof Jedis) { result = (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, values); } return result == 1L; }); } else { return true; } } }

public class LockRes { // 是否拿到锁,false:没拿到,true:拿到 private boolean flag; // 缓存的键 private String key; // 缓存的值 private String value; public LockRes(boolean flag, String key, String value) { super(); this.flag = flag; this.key = key; this.value = value; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } }
转载请注明原文地址: https://www.6miu.com/read-2350028.html

最新回复(0)