jedis分布式锁实现

简介: jedis分布式锁实现

方案1

利用setNX若存在则不插入,不存在则插入成功,同时value为时间戳,没拿到锁则判断时间是否过期,get拿到时间戳,过期则用,getset方式赋值,在比较时间戳是否过期,过期则拿到锁;

@Component
public class RedisLock {
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    public boolean addLock(String key, long expireTime) throws InterruptedException {
        boolean lock = true;
        long now = System.currentTimeMillis();
        //setNx
        while (!stringRedisTemplate.opsForValue().setIfAbsent(key, expireTime + "")) {
            String lastTime = stringRedisTemplate.opsForValue().get(key);
            if (StringUtils.isNotEmpty(lastTime) && Long.parseLong(lastTime) < now) {
                // getset
                String recentTime = stringRedisTemplate.opsForValue().getAndSet(key, expireTime + "");
                //必须判断上一次时间和当前取出来的时间一致才行
                if (StringUtils.isNotEmpty(recentTime) && recentTime.equals(lastTime)) {
                    lock = true;
                    break;
                }
            }
            System.out.println("等待...");
            Thread.sleep(1000);
        }
        return lock;
    }
    public void unLock(String key, long nowTime) {
        //加锁解锁同一个人
        if (nowTime == Long.parseLong(stringRedisTemplate.opsForValue().get(key))) {
            stringRedisTemplate.delete(key);
        }
    }
}

方案2:

加锁:set(key,value,‘NX’,‘EX’,expireTime)

解锁:eval(lua语句,key1,value)

if redis.call("get",KEYS[1]) == ARGV[1]
then
    return redis.call("del",KEYS[1])
else
    return 0
end
@Component
public class RedisFlusterLock {
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    private static final String LUA = "if redis.call(\"get\",KEYS[1]) == ARGV[1]  then "
            + "    return redis.call(\"del\",KEYS[1]) " + "else " + "    return 0 " + "end ";
    public void tryLock(String key,String value, Long expireTime) {
        while (1 == 1) {
            String setResult = stringRedisTemplate.execute((RedisCallback<String>) connection -> {
                Object nativeConnection = connection.getNativeConnection();
                String result = null;
                if (nativeConnection instanceof JedisCluster) {
                    result = ((JedisCluster) nativeConnection).set(key, value, "NX", "EX", expireTime);
                }
                if (nativeConnection instanceof Jedis) {
                    result = ((Jedis) nativeConnection).set(key, value, "NX", "EX", expireTime);
                }
                return result;
            });
            if ("ok".equalsIgnoreCase(setResult)) {
                break;
            } else {
                try {
                    System.out.println("等待...");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public boolean unLock(String key, String value) {
        if (stringRedisTemplate.opsForValue().get(key).equals(value)) {
            Boolean delResult = stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
                Object nativeConnection = connection.getNativeConnection();
                long result = 0L;
                List<String> keys = new ArrayList<>();
                keys.add(key);
                List<String> values = new ArrayList<>();
                values.add(value);
                if (nativeConnection instanceof JedisCluster) {
                    result = (Long) ((JedisCluster) nativeConnection).eval(LUA, keys, values);
                }
                if (nativeConnection instanceof Jedis) {
                    result = (Long) ((Jedis) nativeConnection).eval(LUA, keys, values);
                }
                return result == 1L;
            });
            return delResult;
        } else {
            return false;
        }
    }
}

redis官网


其他参考

基于Redis命令:

SET key valueNX EX max-lock-time  

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

  1. SET命令是原子性操作,NX指令保证只要当key不存在时才会设置value
  2. 设置的value要有唯一性,来确保锁不会被误删(value=系统时间戳+UUID)
  3. 当上述命令执行返回OK时,客户端获取锁成功,否则失败
  4. 客户端可以通过redis释放脚本来释放锁
  5. 如果锁到达了最大生存时间将会自动释放

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

if redis.call("get",KEYS[1]) == ARGV[1]  
then 
  return redis.call("del",KEYS[1]) 
else 
   return 0
end 
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;
    }
}


相关文章
|
NoSQL Java Redis
jedis 与 redission 实现分布式锁
jedis 与 redission 实现分布式锁
356 4
|
NoSQL Java Redis
通过Redis 实现分布式锁_利用Jedis 客户端
通过Redis 实现分布式锁_利用Jedis 客户端
|
分布式计算 NoSQL Java
基于内存的分布式NoSQL数据库Redis(四)Jedis:使用方式
基于内存的分布式NoSQL数据库Redis(四)Jedis:使用方式
304 0
|
存储 消息中间件 缓存
分布式锁中-基于 Redis 的实现需避坑 - Jedis 篇
分布式锁中-基于 Redis 的实现需避坑 - Jedis 篇
653 0
分布式锁中-基于 Redis 的实现需避坑 - Jedis 篇
|
NoSQL Java Redis
Redis的Java客户端Jedis的八种调用方式(事务、管道、分布式…)介绍(转)
[-] 一普通同步方式 二事务方式Transactions 三管道Pipelining 四管道中调用事务 五分布式直连同步调用 六分布式直连异步调用 七分布式连接池同步调用 八分布式连接池异步调用 九需要注意的地方 十测试 十一完整的测试代码 redis是一个著名的key-value存储系统,而作为其官方推荐的java版客户端jedis也非常强大和稳定,支持事务、管道及有jedis自身实现的分布式。
1793 0
|
6月前
|
存储 负载均衡 NoSQL
【赵渝强老师】Redis Cluster分布式集群
Redis Cluster是Redis的分布式存储解决方案,通过哈希槽(slot)实现数据分片,支持水平扩展,具备高可用性和负载均衡能力,适用于大规模数据场景。
456 2
|
6月前
|
存储 缓存 NoSQL
【📕分布式锁通关指南 12】源码剖析redisson如何利用Redis数据结构实现Semaphore和CountDownLatch
本文解析 Redisson 如何通过 Redis 实现分布式信号量(RSemaphore)与倒数闩(RCountDownLatch),利用 Lua 脚本与原子操作保障分布式环境下的同步控制,帮助开发者更好地理解其原理与应用。
409 6
|
7月前
|
存储 缓存 NoSQL
Redis核心数据结构与分布式锁实现详解
Redis 是高性能键值数据库,支持多种数据结构,如字符串、列表、集合、哈希、有序集合等,广泛用于缓存、消息队列和实时数据处理。本文详解其核心数据结构及分布式锁实现,帮助开发者提升系统性能与并发控制能力。
|
11月前
|
数据采集 存储 数据可视化
分布式爬虫框架Scrapy-Redis实战指南
本文介绍如何使用Scrapy-Redis构建分布式爬虫系统,采集携程平台上热门城市的酒店价格与评价信息。通过代理IP、Cookie和User-Agent设置规避反爬策略,实现高效数据抓取。结合价格动态趋势分析,助力酒店业优化市场策略、提升服务质量。技术架构涵盖Scrapy-Redis核心调度、代理中间件及数据解析存储,提供完整的技术路线图与代码示例。
1155 0
分布式爬虫框架Scrapy-Redis实战指南
|
5月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
421 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)

热门文章

最新文章