分布式锁(设置锁和过期时间)
问题描述:
随着业务发展的需要,原单体单机部署的系统被演化分成分布式集群系统后,由于分布式系统多线程、多进程且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题
分布式锁主流的实现方案:
1、基于数据库实现分布式锁
2、基于缓存(Redis等)
3、基于Zookeeper
每一种分布式锁解决方案都有各自的优缺点。
解决方案:基于Redis实现分布式锁
redis:命令
# set sku:1:info "OK" NX PX 10000
EX second:设置键的过期时间为seconds秒,SET key value EX second效果等同于SETEX key second value
NX::只在键不存在时,才对键进行设置操作,SET key value NX效果等同于SETNX key value
XX:只在键已经存在时,才对键进行设置操作
DEL:释放锁
设置过期时间解决锁一直不能释放的问题
上锁之后出现异常无法设置过期时间:上锁时同时设置过期时间 set users 10 nx ex 10 既上锁又设置过期时间同步进行。
@GetMapping("/testLock") public void testLock(){ //1、获取锁, setnx Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111", 3, TimeUnit.SECONDS); //2、获取锁成功,查询num的值 if(lock){ Object value = redisTemplate.opsForValue().get("num"); //2.1 判断num为空return if(StringUtils.isEmpty(value)){ return; } //2.2 有值就转成int int num = Integer.parseInt(value + ""); //2。3 把redis的num加1 redisTemplate.opsForValue().set("num", ++num); //2.4 释放锁,del redisTemplate.delete("lock"); }else { //3 获取锁失败,每个0.1秒再获取 try{ Thread.sleep(100); testLock(); }catch(InterruptedException e){ e.printStackTrace(); } } }
存在问题:误删,锁释放错了
a:先操作
1、上锁
2、具体操作
服务器卡顿
3、锁自动释放
b:抢到锁,具体操作
a服务器反应过来,进行操作,手动释放锁,释放了b的锁
方法:使用UUID表示不同操作
1、set lock uuid nx ex 10
2、释放锁时,首先判断当前uuid和要释放锁的uuid是否一样。
@GetMapping("/testLock") public void testLock(){ String uuid = UUID.randomUUID().toString(); //1、获取锁, setnx Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS); //2、获取锁成功,查询num的值 if(lock){ Object value = redisTemplate.opsForValue().get("num"); //2.1 判断num为空return if(StringUtils.isEmpty(value)){ return; } //2.2 有值就转成int int num = Integer.parseInt(value + ""); //2。3 把redis的num加1 redisTemplate.opsForValue().set("num", ++num); //2.4 释放锁,del、判断比较uuid是否一样 if(redisTemplate.opsForValue().get("lock").equals(uuid)){ redisTemplate.delete("lock"); } }else { //3 获取锁失败,每个0.1秒再获取 try{ Thread.sleep(100); testLock(); }catch(InterruptedException e){ e.printStackTrace(); } } }