redis解决高并发抢购

server/2025/1/9 4:17:05/

对于前后端不分离的程序可以用悲观锁,对于前后端分离的程序可以用redis分布式锁

分布式锁 setnx key value,将key设置为value,当键不存在时,才能成功,若键存在,什么也不做,成功返回1,失败返回0 。 SETNX实际上就是SET IF NOT Exists的缩写

图中当redis分布式锁为1的时候就锁起来,其他线程在外面隔几秒休眠,当起来的时候发现锁为0然后进入项目再上锁,就这样循环

1.前后端不分离抢购:

加上悲观锁synchronized后100个库存有100个人抢到

如果不加的话会有好几百个人抢到

在启动类中初始化商品的key和value:
@SpringBootApplication
public class RedisQgApplication {@Autowiredprivate StringRedisTemplate stringRedisTemplate;//@Bean 可能是因为希望它在 Spring 容器初始化时执行,而不仅仅是一个普通的方法调用。// 如果不加 @Bean,这个方法就不会被 Spring 容器识别并调用。//初始化被抢购商品@Beanpublic void init(){stringRedisTemplate.opsForValue().set("goods:1001", "100");}public static void main(String[] args) {SpringApplication.run(RedisQgApplication.class, args);}
}

在controller里设置抢购程序:

思路:

  1. 判断是否购买过该商品
  2. 判断是否还有库存
  3. 库存减一
  4. 添加用户购买标识
@RestController
@RequestMapping("/index")
public class IndexController {int userId=100;int goodsId=1001;@Autowiredprivate  StringRedisTemplate stringRedisTemplate;@GetMapping("/qg")public synchronized String qg(){userId++;//判断是否已经购买过该商品if (stringRedisTemplate.hasKey("user:"+userId+":"+goodsId)){//user在redis中有key就代表已经购买过了return "已经购买过该商品";}//判断是否还有库存Integer count = Integer.parseInt(stringRedisTemplate.opsForValue().get("goods:"+goodsId));if (count<=0){return "库存不足";}//减去库存count--;stringRedisTemplate.opsForValue().set("goods:"+goodsId, count.toString());//购买标识stringRedisTemplate.opsForValue().set("user:"+userId+":"+goodsId, "0");return "抢购成功";}}

2.前后端分离抢购:

使用SETNX:分布式锁 setnx key value,将key设置为value,当键不存在时,才能成功,若键存在,什么也不做,成功返回1,失败返回0 。 SETNX实际上就是SET IF NOT Exists的缩写

controller:

思路:

  1. 先判断redis分布式锁上了没,上锁了就休眠
  2. 判断是否购买过该商品,买过的走
  3. 判断是否还有库存,没库存了走
  4. 库存减一
  5. 添加用户购买标识,购买完成走
  6. 购买成功后释放锁
@RestController
@RequestMapping("/index")
public class IndexController {int userId=100;int goodsId=1001;@Autowiredprivate RedisUtil redisUtil;@GetMapping("/qg")public synchronized String qg(){userId++;//设置一个redis锁上锁时休眠while (redisUtil.lock("lock_:"+goodsId,10L)){try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}}//判断是否已经购买过该商品if (redisUtil.getStr("user:"+userId+":"+goodsId)!=null){//user在redis中有key就代表已经购买过了return "已经购买过该商品";}String msg;//判断是否还有库存Integer count = Integer.parseInt(redisUtil.getStr("goods:"+goodsId));if (count<=0){msg="库存不足";}else {//减去库存count--;redisUtil.setStr("goods:"+goodsId, count.toString());//购买标识redisUtil.setStr("user:"+userId+":"+goodsId, "0");//购买成功后释放锁redisUtil.unLock("lock_:"+goodsId);msg="抢购成功";}return msg;}
}

工具类:

这个工具类中setnx锁

@Component
public class RedisUtil {@AutowiredStringRedisTemplate stringRedisTemplate;@AutowiredRedisTemplate<Object, Object> redisTemplate;@Resource(name = "stringRedisTemplate")ValueOperations<String, String> valOpsStr;@Resource(name = "redisTemplate")ValueOperations<Object, Object> valOpsObj;/*** 根据指定key获取String* @param key* @return*/public String getStr(String key){return valOpsStr.get(key);}/*** 设置Increment缓存* @param key*/public long getIncrement(String key){return valOpsStr.increment(key);}/*** 设置Increment缓存* @param key* @param val*/public long setIncrement(String key, long val){return valOpsStr.increment(key,val);}/*** 设置Str缓存* @param key* @param val*/public void setStr(String key, String val){valOpsStr.set(key,val);}/**** 设置Str缓存* @param key* @param val* @param expire 超时时间*/public void setStr(String key, String val,Long expire){valOpsStr.set(key,val,expire, TimeUnit.MINUTES);}/*** 删除指定key* @param key*/public void del(String key){stringRedisTemplate.delete(key);}/*** 根据指定o获取Object* @param o* @return*/public Object getObj(Object o){return valOpsObj.get(o);}/*** 设置obj缓存* @param o1* @param o2*/public void setObj(Object o1, Object o2){valOpsObj.set(o1, o2);}/*** 删除Obj缓存* @param o*/public void delObj(Object o){redisTemplate.delete(o);}/**** 加锁的方法* @return*/public boolean lock(String key,Long expire){RedisConnection redisConnection=redisTemplate.getConnectionFactory().getConnection();//设置序列化方法redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(new StringRedisSerializer());if(redisConnection.setNX(key.getBytes(),new byte[]{1})){redisTemplate.expire(key,expire,TimeUnit.SECONDS);redisConnection.close();return true;}else{redisConnection.close();return false;}}/**** 解锁的方法* @param key*/public void unLock(String key){redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(new StringRedisSerializer());redisTemplate.delete(key);}
}

启动类:

同时启动类中的创建key也得替换成redisutil的


@SpringBootApplication
public class RedisQgApplication {@Autowiredprivate RedisUtil redisUtil;//@Bean 可能是因为希望它在 Spring 容器初始化时执行,而不仅仅是一个普通的方法调用。// 如果不加 @Bean,这个方法就不会被 Spring 容器识别并调用。//初始化被抢购商品@Beanpublic void init(){redisUtil.setStr("goods:1001", "100");}public static void main(String[] args) {SpringApplication.run(RedisQgApplication.class, args);}}

3.jmeter应用模拟高并发:

下载apache-jmeter后点开start.bat不要关,等几秒就会弹出这个程序

点击新建

新建线程组

添加查看结果树:这是用来查看结果的


http://www.ppmy.cn/server/156560.html

相关文章

NebulaGraph学习笔记-自定义SessionPool

最近看了一下NebulaGraph图数据库连接的部分资料&#xff0c;发现还可以通过SessionPool的方式连接。下面是一个简单的DEMO记录。 组件项目 相关依赖包 <!-- SpringBoot依赖包 --> <dependency><groupId>org.springframework.boot</groupId><art…

ubuntu 22下解决Unment dependencies问题

问题现象 在使用apt安装包的时候&#xff0c;出现如下错误&#xff1a; 解决方案 第一步 sudo apt-get -f install sudo apt-get update sudo apt-get upgrade第二步 sudo apt-get update sudo apt-get clean sudo apt-get autoremove第三步 sudo apt --fix-broken inst…

行政审批远程勘验管理系统(源码+文档+部署+讲解)

引言 在快速发展的现代社会&#xff0c;远程踏勘系统作为一项创新技术&#xff0c;正逐渐改变传统的现场勘察工作模式。本文将详细介绍远程踏勘系统的核心功能、技术优势以及它如何提升现场勘察的效率。 系统概述 远程踏勘系统采用前后端分离的架构设计&#xff0c;服务端基…

【漏洞分析】DDOS攻防分析(二)

0x00 HTTP DDOS攻击实例解析 2014年5月&#xff0c;颇负盛名的搜狐视频&#xff0c;背负了一起著名的DDoS攻击事件。 当时&#xff0c;日本CDN服务商Incapsula声称&#xff0c;自己的一位客户的服务器遭遇了搜狐视频发起的DDoS攻击&#xff0c;期间总共有超过2万的网民通过搜…

HTML5新特性|05 CSS3边框CSS3背景

CSS3边框 1、CSS3边框: 通过CSS3&#xff0c;您能够创建圆角边框&#xff0c;向矩形添加阴影&#xff0c;使用图片来绘制边框-并且不需使用设计软件&#xff0c;比如PhotoShop。 属性: border-radius 圆角box-shadow:水平阴影 垂直阴影 阴影的清晰度 阴影的大小 阴影的颜色…

python+PyMuPDF库:(三)pdf文件的选择性合并、其他格式文件转pdf

insert_file: 支持docx、xlsx、pdf、PPTX、txt、svg、xps、FB2、CBZ、EPUB、MOBI、HWPX、图片等多种格式的文件的插入。利用此方法可以将此类文件转为pdf格式的文件。 insert_pdf &#xff1a;用来打开pdf文件并插入。 insert_file和insert_pdf使用相同的参数&#xff0c;常用…

【设计模式】 基本原则、设计模式分类

设计模式 设计模式是软件工程中的一种通用术语&#xff0c;指的是针对特定问题的经过实践验证的解决方案。设计模式并不是最终的代码实现&#xff0c;而是描述了如何解决某一类问题的思路和方法。 如果熟悉了设计模式&#xff0c;当遇到类似的场景&#xff0c;我们可以快速地…

数据结构-栈与队列笔记

普通的双端队列 验证图书取出顺序 class Solution {/*** 验证书籍的借阅顺序是否合法。* * param putIn 表示放入书架的书籍序列。* param takeOut 表示从书架取出的书籍序列。* return 如果书籍的借阅顺序合法&#xff0c;返回 true&#xff1b;否则返回 false。*/public boo…