#### redis高并发秒杀测试
> 测试项目: https://github.com/14251104246/redis-demo.git
#### 准备
- 使用`docker-compose`命令启动redis服务器(可以用其他方式启动)
- idea启动测试项目
- jmeter测试脚本
- [高并发秒杀-重现超卖问题.jmx](https://github.com/14251104246/redis-demo/blob/master/src/test/jmeter/%E9%AB%98%E5%B9%B6%E5%8F%91%E7%A7%92%E6%9D%80-%E9%87%8D%E7%8E%B0%E8%B6%85%E5%8D%96%E9%97%AE%E9%A2%98.jmx)
- [高并发秒杀-有事务方式减少库存.jmx](https://github.com/14251104246/redis-demo/blob/master/src/test/jmeter/%E9%AB%98%E5%B9%B6%E5%8F%91%E7%A7%92%E6%9D%80-%E6%9C%89%E4%BA%8B%E5%8A%A1%E6%96%B9%E5%BC%8F%E5%87%8F%E5%B0%91%E5%BA%93%E5%AD%98.jmx)
#### 重现秒杀时出现的超卖问题
- 核心测试代码如下:
```java
/**
* 用于测试redis秒杀
*/
@RestController
@RequestMapping("/api/spike")
@Slf4j
public class SpikeController {
@Resource(name = "stringRedisTemplate")
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedissonClient redissonClient;
//记录实际卖出的商品数量
private AtomicInteger successNum = new AtomicInteger(0);
@RequestMapping(value = "/initSku", method = RequestMethod.GET)
public String initSku() {
//初始化库存数量
stringRedisTemplate.opsForValue().set("product_sku", "5");
//初始化实际卖出的商品数量0
successNum.set(0);
return "初始化库存成功";
}
/**
* 会出现超卖情况的减少库存方式
* @return
*/
@RequestMapping(value = "/reduceSku", method = RequestMethod.GET)
public String reduceSku() {
Integer sku = Integer.parseInt(stringRedisTemplate.opsForValue().get("product_sku"));
sku = sku - 1;
if (sku < 0) {
return "库存不足";
}
stringRedisTemplate.opsForValue().set("product_sku", sku.toString());
//记录实际卖出的商品数量
return "减少库存成功,共减少" + successNum.incrementAndGet();
}
@RequestMapping(value = "/successNum", method = RequestMethod.GET)
public String successNum() {
return "顾客成功抢到的商品数量:" + successNum.get();
}
}
```
- 测试api:
```
API{初始化库存数量} >> http://127.0.0.1:8090/api/spike/initSku
API{减少库存数量} >> http://127.0.0.1:8090/api/spike/reduceSku
API{查看共减少库存数量} >> http://127.0.0.1:8090/api/spike/successNum
```
- 第一个api用于:初始化库存中的商品数量为5
- 第二个api用于:减少库存1个商品(即客户购买一个商品)
- 第三个api用于:查看用户实际购买的商品
- 少量用户请求的情况展示:
- 首先初始商品库存:http://127.0.0.1:8090/api/spike/initSku
>![image.png](https://upload-images.jianshu.io/upload_images/7176877-569fb412ed3204c6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- redis数据库中商品库存记录,结果为5
>![image.png](https://upload-images.jianshu.io/upload_images/7176877-3c373cf1058a0da6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 查看用户实际购买的商品,结果为0
>![image.png](https://upload-images.jianshu.io/upload_images/7176877-a2019253556b9b5f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 客户购买5次商品(调用5次`减少库存数量`api),下面只列出3个图
>![image.png](https://upload-images.jianshu.io/upload_images/7176877-5e210855a352cdb8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
>![image.png](https://upload-images.jianshu.io/upload_images/7176877-3c27d4abc48392c4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
>![image.png](https://upload-images.jianshu.io/upload_images/7176877-cce7dad4d0f446f8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 客户继续购买(继续调用`减少库存数量`api)时,会提示库存不足
- 再次查看redis数据库中商品库存记录,结果为0
> ![image.png](https://upload-images.jianshu.io/upload_images/7176877-c2c4f000761a989e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 再次查看用户实际购买的商品,结果为5
> ![image.png](https://upload-images.jianshu.io/upload_images/7176877-6afe5f8e1efeef59.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 大量用户请求的情况(高并发秒杀)展示
- 首先初始商品库存:http://127.0.0.1:8090/api/spike/initSku
>![image.png](https://upload-images.jianshu.io/upload_images/7176877-569fb412ed3204c6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- redis数据库中商品库存记录,结果为5
>![image.png](https://upload-images.jianshu.io/upload_images/7176877-3c373cf1058a0da6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 查看用户实际购买的商品,结果为0
>![image.png](https://upload-images.jianshu.io/upload_images/7176877-a2019253556b9b5f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 使用jmeter打开测试脚本,可以看到基本配置如下
> ![image.png](https://upload-images.jianshu.io/upload_images/7176877-6e396d593cf087c1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- jmeter并发配置如下(当用户数达到 1000 的时候才开始测试)
> ![image.png](https://upload-images.jianshu.io/upload_images/7176877-f986edb5bb69f971.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 点击jmeter的`start`按钮,开始1000个并发请求
- 再次查看redis数据库中商品库存记录,结果为0
> ![image.png](https://upload-images.jianshu.io/upload_images/7176877-c2c4f000761a989e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- **注意:再次查看用户实际购买的商品,结果超过5,出现超卖情况!!!**
> ![image.png](https://upload-images.jianshu.io/upload_images/7176877-912903c714c61d78.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#### 超卖问题原因分析
- 从上面测试结果,我们知道,高并发请求`http://127.0.0.1:8090/api/spike/reduceSku`,会出现超卖的情况
- 下面我们看下超卖问题的原因
```
/**
* 会出现超卖情况的减少库存方式
* @return
*/
@RequestMapping(value = "/reduceSku", method = RequestMethod.GET)
public String reduceSku() {
Integer sku = Integer.parseInt(stringRedisTemplate.opsForValue().get("product_sku"));
sku = sku - 1;
if (sku < 0) {
return "库存不足";
}
stringRedisTemplate.opsForValue().set("product_sku", sku.toString());
//记录实际卖出的商品数量
return "减少库存成功,共减少" + successNum.incrementAndGet();
}
```
- 从代码片可以看出,问题原因是库存数量`sku`的读和写操作不在同一个原子操作上,导致类似`不可重复读`的现象。可以类比多线程的问题。
#### 通过redis事务解决超卖问题
##### 使用redis原生的sdk
- 如下改造`reduceSku()`方法,作为一个新接口`http://127.0.0.1:8090/api/spike/reduceSku3`
```
/**
* 加入事务的减少库存方式
* @return
*/
@RequestMapping(value = "/reduceSku3", method = RequestMethod.GET)
public String reduceSku3() {
Jedis jedis = new Jedis("127.0.0.1", 6379);
List<Object> result ;
Transaction transaction = null;
try {
jedis.watch("product_sku");
int sku = Integer.parseInt(jedis.get("product_sku"));
if (sku > 0) {
transaction = jedis.multi();
transaction.set("product_sku", String.valueOf(sku - 1));
// int exp = 1/0;
result = transaction.exec();
if (result == null || result.isEmpty()) {
徐浪老师
- 粉丝: 8554
- 资源: 1万+
最新资源
- 单片机温度控制器,基于pid算法的半导体温控系统 PID智能温控系统proteus仿真,能升温、降温、控温;LCD显示设置温度与实时温度;请悉知,资料包含程序源码(stm32库函数),Proteus
- GD链条-模块对应表(X1)
- java(选修)复习资料.7z
- (cuda12.4)mamba-ssm-2.2.2-cp310-cp310-win-amd64.whl
- google-chrome-stable-current-x86-64.rpm
- 一套WPF+.net6 WebApiv+SqlSugar ORM 权限管理平台源代码,全源码,仅限系统管理部分源代码C#WPF也是当前智能制造企业研发工控系统研发的首选,这套系统也是学习与研发的首选
- GD链条-模块对应表(X2)
- 2. java-TM(实例源码+习题答案).rar
- google chrome 64位-98.0.4758.82.exe
- 数组冒泡排序程序 博途v16编写的西门子排序程序,可实现不同长度的数组排序,可根据需求选择从大到小还是从小到大排序 封装好的FC块直接可以拿来学习,并且配有注释可轻松学习
- Cursor小工具 需要一定动手能力的友友下载
- 3. java-PPT课件(可供参考).rar
- 全国水体分布shp矢量数据集
- GDL语言中文说明使用书
- 一种粒子群优化算法优化深度极限学习机DELM中的各极限学习机中自动编码器的输入权重与偏置,建立PSO-DELM回归预测模型,多输入单输出模型,时间窗法,代码注释清晰,替数据简单,只需替自己的excel
- 舒尔特方格 暑期学校mfc大作业 mfc.zip
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈