博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
redis实现分布式锁
阅读量:5756 次
发布时间:2019-06-18

本文共 3109 字,大约阅读时间需要 10 分钟。

一、什么是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依赖

redis.clients
jedis
2.9.0
复制代码

加锁tryLock

public boolean tryLock(String lockKey, String requestId, long expire) {        Boolean result = redisTemplate.execute((RedisCallback
) 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
keys = new ArrayList<>(); keys.add(lockKey); List
args = new ArrayList<>(); args.add(requestId); Long result = redisTemplate.execute((RedisCallback
) 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相等,如果相等则删除锁(解锁)。

转载于:https://juejin.im/post/5b9381ace51d450e7d097d7b

你可能感兴趣的文章