一、什么是redis
redis是一个基于内存的高性能key-value数据库
二、redis基本数据类型及应用场景
- string(字符串):String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。
- list(列表) :使用Lists结构,我们可以轻松地实现最新消息排行等功能。List的另一个应用就是消息队列,可以利用List的PUSH操作,将任务存在List中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作List中某一段的api,你可以直接查询,删除List中某一段的元素。 Redis的list是每个子元素都是String类型的双向链表,可以通过push和pop操作从列表的头部或者尾部添加或者删除元素,这样List即可以作为栈,也可以作为队列。
- hash(散列):Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 存储部分变更的数据,如用户信息等。
- sets (集合) : 利用Redis提供的set数据结构,可以存储一些集合性的数据。set中的元素是没有顺序的。
- 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)的参数详解如下:
- key:使用key来当锁,可以使用代表当前请求的唯一id进行加锁。
- value:即为requestId,通过给value赋值为requestId,解锁的时候通过同一个requestId解锁。
- nxxx:NX为SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;XX为key存在则修改其值。
- expx:给key加一个过期时间,具体时间由第五个参数决定。EX seconds。PX milliseconds。
- 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(); Listkeys = 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相等,如果相等则删除锁(解锁)。