⑥. 单机的Redis案例加锁、解锁
①. 加锁:加锁实际上就是在redis中,给Key键设置一个值,为避免死锁,并给定一个过期时间
②. 解锁:将Key键删除。但也不能乱删,不能说客户端1的请求将客户端2的锁给删除掉,只能自己删除自己的锁
(为了保证解锁操作的原子性,我们用LUA脚本完成这一操作。先判断当前锁的字符串是否与传入的值相等,是的话就删除Key,解锁成功)
if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end
- ③. 超时:锁key要注意过期时间,不能长期占用
- ④. 单机模式中,一般都是用set/setnx+lua脚本搞定,想想它的缺点是什么?
如果redis发生了宕机,所有的请求压力都指向了数据库
public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() { String uuid = UUID.randomUUID().toString(); ValueOperations<String, String> ops = stringRedisTemplate.opsForValue(); Boolean lock = ops.setIfAbsent("lock", uuid,500, TimeUnit.SECONDS); if (lock) { Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap(); String lockValue = ops.get("lock"); // get和delete原子操作 String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" + " return redis.call(\"del\",KEYS[1])\n" + "else\n" + " return 0\n" + "end"; stringRedisTemplate.execute( new DefaultRedisScript<Long>(script, Long.class), // 脚本和返回类型 Arrays.asList("lock"), // 参数 lockValue); // 参数值,锁的值 return categoriesDb; }else { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // 睡眠0.1s后,重新调用 //自旋 return getCatalogJsonDbWithRedisLock(); } }