当前位置: 首页 > news >正文

redis分布式锁

文章目录

  • 案例:秒杀
  • 优化一:JVM锁synchronized、ReentrantLock
  • 优化二:分布式锁实现之setnx
  • 优化三:分布式锁实现之redisson
  • 优化四:分布式锁实现之redlock

案例:秒杀

/* * 版本一:单机版模拟商品秒杀 * 问题:单机版没有加锁,高并发导致商品超卖现象 * 解决:加锁呗,jvm级别的锁可以是synchronized、ReentrantLock,加哪一个呢,有什么区别? * 1. synchronized:锁住整个方法/代码块,不能灵活的释放锁,其他线程等不到锁只能死等 * 2. ReentrantLock:锁住代码块,可以通过tryLock()方法,设置超时时间,超时后灵活自动释放锁 * * 如何选择: * 根据业务需求来选,如果非要抢到锁不可,就使用 synchronized 锁; * 如果可以暂时放弃锁,等会再来强,就使用 ReentrantLock 锁 */@GetMapping("/buyGoods")publicStringbuyGoods(){// 1. 从redis中获取商品的剩余数量Objectresult=redisUtils.get("goods:001");Integernum=Objects.isNull(result)?0:Integer.parseInt(result.toString());// 2. 逻辑处理StringretStr=null;if(num>0){// 商品数量大于零才能出售num=num-1;redisUtils.set("goods:001",num);retStr="你已经成功秒杀商品,此时还剩余:"+num+"件"+" 服务器端口: "+serverPort;}else{// 商品售罄retStr="商品已经售罄/活动结束/调用超时,欢迎下次光临"+" 服务器端口: "+serverPort;}System.out.println(retStr);returnretStr;}

优化一:JVM锁synchronized、ReentrantLock

/** * 版本二:通过synchronized、ReentrantLock锁解决单机版商品秒杀问题 * 问题:仅适用于单机环境,在分布式环境中,因为锁的竞争不在同一JVM中,仍然出现超卖现象。所以需要一个让所有线程都能访问的分布式锁来实现,比如redis * 解决方案:redis分布式锁各种实现方案 * * @return */@GetMapping("/buyGoods2")publicStringbuyGoods2(){synchronized(this){Objectresult=redisUtils.get("goods:001");Integernum=Objects.isNull(result)?0:Integer.parseInt(result.toString());StringretStr=null;if(num>0){num=num-1;redisUtils.set("goods:001",num);retStr="你已经成功秒杀商品,此时还剩余:"+num+"件"+" 服务器端口: "+serverPort;}else{retStr="商品已经售罄/活动结束/调用超时,欢迎下次光临"+" 服务器端口: "+serverPort;}System.out.println(retStr);returnretStr;}}

优化二:分布式锁实现之setnx

/** * 版本三:分布式锁实现之SETNX * 原理:在多个redis客户端同事通过setnx尝试获取锁,利用redis单线程的特性,可以保证只会有一个客户端成功获取到锁,其他客户端泽失败,并且获取锁成功的客户端,会设置一个过期时间,避免该客户端挂了之后一直持有该锁 * 功能: * 1.获取锁,锁的过期时间设置为10秒,如果获取锁失败,则返回失败,并且保证加锁和设置过期时间为原子操作。 * 存在问题1:加锁如果设置过期时间,可能导致锁误删除,如果不设置过期时间,可能导致死锁 * 存在问题2:不支持可重入,可借助redisson实现可重入锁 * @return */@GetMapping("/buyGoods3")publicStringbuyGoods3(){try{// 1. 获取锁【加锁+设置过期时间原子性,并且不要把加锁和设置过期时间分开写】BooleanlockFlag=redisUtils.setIfAbsent(LOCK_KEY,UUID.randomUUID()+Thread.currentThread().getName(),10L);if(!lockFlag){return"抢锁失败";}// 2. 业务逻辑处理Objectresult=redisUtils.get("goods:001");Integernum=Objects.isNull(result)?0:Integer.parseInt(result.toString());StringretStr=null;if(num>0){num=num-1;redisUtils.set("goods:001",num);retStr="你已经成功秒杀商品,此时还剩余:"+num+"件"+" 服务器端口: "+serverPort;}else{retStr="商品已经售罄/活动结束/调用超时,欢迎下次光临"+" 服务器端口: "+serverPort;}System.out.println(retStr);returnretStr;}finally{// 3. 释放锁:判断是否是自己加的锁if(value.equalsIgnoreCase((String)redisUtils.get(LOCK_KEY))){redisUtils.del(LOCK_KEY);// 也可以通过lua脚本实现删除操作原子性}}}

优化三:分布式锁实现之redisson

/** * 版本七:redisson最终版 * 1. 分布式锁的加锁、解锁原子性 * 2. 锁的续期 */@GetMapping("/buyGoods7")publicStringbuyGoods7()throwsException{// 1. 获取可重入的分布式锁【获取到锁的线程可以再次尝试获取锁,并且只能被占用锁的线程解锁】RLockredissonLock=redissonClient.getLock(LOCK_KEY);redissonLock.lock(10,TimeUnit.SECONDS);// 2. 业务逻辑处理try{Objectresult=redisUtils.get("goods:001");Integernum=Objects.isNull(result)?0:Integer.parseInt(result.toString());StringretStr=null;if(num>0){intrealNumber=num-1;redisUtils.set("goods:001",realNumber+"");retStr="你已经成功秒杀商品,此时还剩余:"+realNumber+"件"+"\t 服务器端口: "+serverPort;}else{retStr="商品已经售罄/活动结束/调用超时,欢迎下次光临"+"\t 服务器端口: "+serverPort;}System.out.println(retStr);returnretStr;}finally{// 3. 解锁,还在持有锁的状态,并且是当前线程持有的锁再解锁if(redissonLock.isLocked()&&redissonLock.isHeldByCurrentThread()){redissonLock.unlock();}}}

redissonLock.lock(10, TimeUnit.SECONDS)底层原理:

  1. 首先通过lua脚本给key加锁,设置过期时间

  1. 其次通过定时任务,不停检测锁是否到期,自动续期

那redisson实现分布式锁就没有缺点了吗,当然不是,redisson默认一个redis节点加锁成功就可以了,然后同步给其他节点,但是如果在master节点加锁成功,然而还没来得及同步到子节点就挂了,那其他线程就可以在新的master节点在获取到锁!!

优化四:分布式锁实现之redlock

✅什么是RedLock,他解决了什么问题?

以前默认一个redis节点加锁成功就可以,但是有问题,redlock优化为一般一办以上的redis节点加锁成功才可以,避免单点故障

http://www.jsqmd.com/news/676794/

相关文章:

  • 告别安装报错!保姆级Quartus II 13.1 + ModelSim联调配置指南(附资源与避坑清单)
  • FanControl终极中文配置指南:5分钟实现专业级风扇控制
  • MATLAB+Yalmip+Gurobi一站式配置与实战验证指南
  • 湖北致信通建筑:宜昌专业的淤泥清理管道疏通 - LYL仔仔
  • 如何正确管理浮层提示(Tooltip)显示时的页面焦点顺序
  • 从标注到部署:手把手教你用Labelme标注数据并转COCO格式,喂给SOLOv2做实例分割
  • 【Excel提效 No.004】一句话搞定按条件拆分为多个独立Excel文件
  • FastLED终极指南:5分钟上手专业级Arduino LED动画库
  • 杭州银鑫物资回收:上城机电设备回收价格 - LYL仔仔
  • Thorium Reader终极指南:如何实现跨平台电子书的高效管理与沉浸式阅读
  • 十八.解决写索引代码报异常问题
  • KeymouseGo:零代码自动化神器,轻松告别重复性鼠标键盘操作
  • granite-4.0-h-350m实战案例:Ollama部署用于企业IT运维智能问答助手
  • 告别虚拟机!用一台旧电脑打造你的专属Ubuntu远程开发桌面(VNC实战)
  • 3分钟解锁中文设计:FigmaCN如何让你的设计效率提升50%
  • 从校园网到手机热点:Kali桥接模式联网的两种实战场景与配置差异详解
  • 告别fix bond/react:手写Python交联脚本,让你的LAMMPS聚合物模拟更精准
  • 锐捷交换机VSU配置保姆级教程:从物理连线到BFD检测,手把手带你搞定双机虚拟化
  • 用Python搞定VIC模型数据制备:一个脚本搞定网格、土壤、植被和气象强迫
  • 嵌入式系统I/O与并发编程核心技术解析
  • Win11Debloat:一键清理Windows 11预装垃圾,让你的系统快如闪电 [特殊字符]
  • 嵌入式Linux下Qt/Qml横竖屏适配踩坑记:从export环境变量到手动旋转Item的完整解决方案
  • 如何用Applite在10分钟内告别Mac软件安装的烦恼?
  • Qt项目实战:用SQLiteCipher插件给本地数据库加把锁(附多数据库Attach避坑指南)
  • 【Claude Code 源码解析教程】第8章:文件操作工具
  • 从AtomicInteger到自旋锁:深入剖析CAS的实战演进与性能调优
  • Rust与RP2040实现专业咖啡机PID控制
  • 《Improving RGB-infrared object detection with cascade alignment-guided transformer》论文分享(侵删)
  • CDN隐匿下的真实IP溯源:实战绕过策略与场景解析
  • Navicat Premium试用期重置终极指南:简单三步恢复14天完整试用