Redis实现分布式锁

一、什么是redis

redis是一个基于内存的高性能key-value数据库

二、redis基本数据类型及应用场景

  1. string(字符串):String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。
  2. list(列表) :使用Lists结构,我们可以轻松地实现最新消息排行等功能。List的另一个应用就是消息队列,可以利用List的PUSH操作,将任务存在List中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作List中某一段的api,你可以直接查询,删除List中某一段的元素。 Redis的list是每个子元素都是String类型的双向链表,可以通过push和pop操作从列表的头部或者尾部添加或者删除元素,这样List即可以作为栈,也可以作为队列。
  3. hash(散列):Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 存储部分变更的数据,如用户信息等。
  4. sets (集合) : 利用Redis提供的set数据结构,可以存储一些集合性的数据。set中的元素是没有顺序的。
  5. sorted set(有序集合):和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列,比如一个存储全班同学成绩的sorted set,其集合value可以是同学的学号,而score就可以是其考试得分,这样在数据插入集合的时候,就已经进行了天然的排序。可以用sorted set来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。

三、代码实现

maven依赖

<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
</dependency>
复制代码

加锁tryLock

public boolean tryLock(String lockKey, String requestId, long expire) {
        Boolean result = redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            JedisCommands commands = (JedisCommands) connection.getNativeConnection();
            return StringUtils.isNotBlank(commands.set(lockKey, requestId, "NX", "PX", expire));
        });
        return result;
    }
复制代码

jedis.set(String key, String value, String nxxx, String expx, int time)的参数详解如下:

  1. key:使用key来当锁,可以使用代表当前请求的唯一id进行加锁。
  2. value:即为requestId,通过给value赋值为requestId,解锁的时候通过同一个requestId解锁。
  3. nxxx:NX为SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;XX为key存在则修改其值。
  4. expx:给key加一个过期时间,具体时间由第五个参数决定。EX seconds。PX milliseconds。
  5. time:代表key的过期时间。

解锁unlock

public boolean unLock(String lockKey, String requestId) {
        // 使用lua脚本删除redis中匹配value的key,可以避免由于方法执行时间过长而redis锁自动过期失效的时候误删其他线程的锁
        // spring自带的执行脚本方法中,集群模式直接抛出不支持执行脚本的异常,所以只能拿到原redis的connection来执行脚本
        StringBuilder sb = new StringBuilder();
        sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
        sb.append("then ");
        sb.append("    return redis.call(\"del\",KEYS[1]) ");
        sb.append("else ");
        sb.append("    return 0 ");
        sb.append("end ");
        String unLockLua = sb.toString();

        List<String> keys = new ArrayList<>();
        keys.add(lockKey);
        List<String> args = new ArrayList<>();
        args.add(requestId);

        Long result = redisTemplate.execute((RedisCallback<Long>) connection -> {
            Object nativeConnection = connection.getNativeConnection();
            // 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行
            // 集群模式
            if (nativeConnection instanceof JedisCluster) {
                return (Long) ((JedisCluster) nativeConnection).eval(unLockLua, keys, args);
            }

            // 单机模式
            else if (nativeConnection instanceof Jedis) {
                return (Long) ((Jedis) nativeConnection).eval(unLockLua, keys, args);
            }
            return 0L;
        });
        return result != null && result > 0;
    }
复制代码

使用jedis.eval()执行Lua代码,Lua脚本的含义为:首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值