此方法主要理解 分布式锁工作原理:推荐使用
学习redisson 了解工作原理:
https://blog.csdn.net/qq_17040587/article/details/121516553
终极方案用springCache的例子
https://blog.csdn.net/qq_17040587/article/details/121522421
简单分布式锁(基于redis)
此文章限于理解分布式工作原理,实际开发推荐后续文章的the Redlock 设计.java对应的是Redisson.
核心:原子加锁 原子解锁
加锁相关redis语句:
set lock 1 NX 解释:lock键无占用才设置键lock值为1 set lock 1 EX 300 NX 解释:lock键无占用时才设置键lock值为1 有效时间为:300s
解锁相关语句:
if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end
思路过程代码:
public Map> getCatalogJsonFromDbWithRedisLock() { //1.占分布式锁 去redis占坑 【set lock 1 EX 30 NX】 lock键无占用时才设置键lock值为1 有效时间为:300s String uuid = UUID.randomUUID().toString(); //设置300s的过期时间 放置执行业务过程中 锁过期了。 让过期时间靠谱的大约业务时间 后面再执行脚本解锁 Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(RedisConstant.INDEX_CATALOGJSON_LOCK, uuid,300,TimeUnit.SECONDS); if(lock){ System.out.println("获取分布式锁成功"); //加锁成功 ... 执行业务 Map > dataFromDb = null; try{ dataFromDb = getDataFromDb(); }finally { //保证删除的锁 是自己的锁再删除 // 判断的原因:lock过期后,会有一个请求x可以进来,如果此时删锁,会导致x的占位失败会导致多个人进来 //获取值锁 远程 //String lockValue = stringRedisTemplate.opsForValue().get(RedisConstant.INDEX_CATALOGJSON_LOCK); //瞬时异常: lockValue去请求返回的路上过期 lockValue带回来的确实是自己存的内容,此时lock已经被人占用成功,也会误删别人的锁 // if(uuid.equals(lockValue)){ //解锁远程 // stringRedisTemplate.delete(RedisConstant.INDEX_CATALOGJSON_LOCK); //删锁失败 造成思索 // } // ↓ 优化 //获取值锁 + 对比成功删除 合并原子 *** 作 脚本Lua脚本解锁 String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end"; Long unlock = stringRedisTemplate.execute(new DefaultRedisscript (script, Long.class), Arrays.asList(RedisConstant.INDEX_CATALOGJSON_LOCK), uuid); } return dataFromDb; }else{ System.out.println("获取分布式锁失败 等待重试.."); //加锁失败 ...重试 synchronized() //休眠100ms重试 try { Thread.sleep(200); }catch ( Exception e){ } return getCatalogJsonFromDbWithRedisLock();// 自旋方式 } }
两个必要性原子 *** 作:
加锁 :设置锁独占 设置过期时间
解锁:查询锁值 删除所
整合:
public Map> getCatalogJsonFromDbWithRedisLock() { //1.占分布式锁 去redis占坑 【set lock 1 EX 30 NX】 lock键无占用时才设置键lock值为1 有效时间为:300s String uuid = UUID.randomUUID().toString(); //设置300s的过期时间 放置执行业务过程中 锁过期了。 让过期时间靠谱的大约业务时间 后面再执行脚本解锁 Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(RedisConstant.INDEX_CATALOGJSON_LOCK, uuid,300,TimeUnit.SECONDS); if(lock){ System.out.println("获取分布式锁成功"); //加锁成功 ... 执行业务 Map > dataFromDb = null; try{ dataFromDb = getDataFromDb(); }finally { //获取值锁 + 对比成功删除 合并原子 *** 作 脚本Lua脚本解锁 String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end"; Long unlock = stringRedisTemplate.execute(new DefaultRedisscript (script, Long.class), Arrays.asList(RedisConstant.INDEX_CATALOGJSON_LOCK), uuid); } return dataFromDb; }else{ System.out.println("获取分布式锁失败 等待重试.."); //加锁失败 ...重试 synchronized() //休眠100ms重试 try { Thread.sleep(200); }catch ( Exception e){ } return getCatalogJsonFromDbWithRedisLock();// 自旋方式 } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)