jedis分布式锁实现

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 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;
    }
}


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
5月前
|
NoSQL Java Redis
jedis 与 redission 实现分布式锁
jedis 与 redission 实现分布式锁
126 4
|
6月前
|
NoSQL Java Redis
通过Redis 实现分布式锁_利用Jedis 客户端
通过Redis 实现分布式锁_利用Jedis 客户端
|
7月前
|
分布式计算 NoSQL Java
基于内存的分布式NoSQL数据库Redis(四)Jedis:使用方式
基于内存的分布式NoSQL数据库Redis(四)Jedis:使用方式
225 0
|
存储 消息中间件 缓存
分布式锁中-基于 Redis 的实现需避坑 - Jedis 篇
分布式锁中-基于 Redis 的实现需避坑 - Jedis 篇
373 0
|
NoSQL Java Redis
Redis的Java客户端Jedis的八种调用方式(事务、管道、分布式…)介绍(转)
[-] 一普通同步方式 二事务方式Transactions 三管道Pipelining 四管道中调用事务 五分布式直连同步调用 六分布式直连异步调用 七分布式连接池同步调用 八分布式连接池异步调用 九需要注意的地方 十测试 十一完整的测试代码 redis是一个著名的key-value存储系统,而作为其官方推荐的java版客户端jedis也非常强大和稳定,支持事务、管道及有jedis自身实现的分布式。
1521 0
|
2月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
4月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
132 2
基于Redis的高可用分布式锁——RedLock
|
23天前
|
存储 NoSQL Java
使用lock4j-redis-template-spring-boot-starter实现redis分布式锁
通过使用 `lock4j-redis-template-spring-boot-starter`,我们可以轻松实现 Redis 分布式锁,从而解决分布式系统中多个实例并发访问共享资源的问题。合理配置和使用分布式锁,可以有效提高系统的稳定性和数据的一致性。希望本文对你在实际项目中使用 Redis 分布式锁有所帮助。
70 5
|
27天前
|
NoSQL Java 数据处理
基于Redis海量数据场景分布式ID架构实践
【11月更文挑战第30天】在现代分布式系统中,生成全局唯一的ID是一个常见且重要的需求。在微服务架构中,各个服务可能需要生成唯一标识符,如用户ID、订单ID等。传统的自增ID已经无法满足在集群环境下保持唯一性的要求,而分布式ID解决方案能够确保即使在多个实例间也能生成全局唯一的标识符。本文将深入探讨如何利用Redis实现分布式ID生成,并通过Java语言展示多个示例,同时分析每个实践方案的优缺点。
58 8
|
1月前
|
NoSQL Redis
Redis分布式锁如何实现 ?
Redis分布式锁通过SETNX指令实现,确保仅在键不存在时设置值。此机制用于控制多个线程对共享资源的访问,避免并发冲突。然而,实际应用中需解决死锁、锁超时、归一化、可重入及阻塞等问题,以确保系统的稳定性和可靠性。解决方案包括设置锁超时、引入Watch Dog机制、使用ThreadLocal绑定加解锁操作、实现计数器支持可重入锁以及采用自旋锁思想处理阻塞请求。
59 16

热门文章

最新文章