本文主要是介绍(一)库存超卖案例实战——库存超卖现象的产生及其解决方案概述,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
前言
本节内容我们主要介绍一下web应用中常见的一类问题——产品“超卖”问题,通过一个springboot项目案例完成超卖现象的演示,并针对不同的应用场景下,提供这一类问题的解决方案,关于更详细的解决方案案例实战内容,请关注作者后期的博客内容。
正文
①创建一个商品库存表,用于商品库存的存储
CREATE TABLE `wms_stock` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',`stock_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '仓库编号',`product_code` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '产品',`stock_quantity` int DEFAULT NULL COMMENT '库存数量',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='库存';
②商品库存表中增加一条库存数据用于库存数据的测试
INSERT INTO `wms_stock` (`id`, `stock_code`, `product_code`, `stock_quantity`) VALUES (1, 'A1000', 'P2000', 10000);
③ springboot项目中创建一个WmsStockController类,实现一个扣减库存的请求
package com.ht.atp.plat.controller;import com.ht.atp.plat.common.Result;
import com.ht.atp.plat.service.WmsStockService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** <p>* 库存 前端控制器* </p>*/
@Api(tags = {"库存管理"})
@RestController
@RequestMapping("/wms/stock")
public class WmsStockController {@Autowiredprivate WmsStockService wmsStockService;@ApiOperation("检查并扣减库存")@GetMapping("checkAndReduceStock")public Result checkAndReduceStock() {wmsStockService.checkAndReduceStock();return Result.success();}}
④ springboot项目中创建一个WmsStockService接口类,实现一个扣减库存的业务接口
package com.ht.atp.plat.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.ht.atp.plat.entity.WmsStock;/*** <p>* 库存 服务类* </p>*/
public interface WmsStockService extends IService<WmsStock> {/*** 检查并扣减库存*/void checkAndReduceStock();}
⑤ springboot项目中创建一个WmsStockServiceImpl实现类,实现一个扣减库存的业务实现
package com.ht.atp.plat.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ht.atp.plat.entity.WmsStock;
import com.ht.atp.plat.mapper.WmsStockMapper;
import com.ht.atp.plat.service.WmsStockService;
import org.springframework.stereotype.Service;/*** <p>* 库存 服务实现类* </p>*/
@Service
public class WmsStockServiceImpl extends ServiceImpl<WmsStockMapper, WmsStock> implements WmsStockService {@Overridepublic void checkAndReduceStock() {// 查询库存WmsStock wmsStock = baseMapper.selectById(1L);// 验证库存大于0再扣减库存if (wmsStock != null && wmsStock.getStockQuantity() > 0) {wmsStock.setStockQuantity(wmsStock.getStockQuantity() - 1);baseMapper.updateById(wmsStock);}}
}
⑥启动测试项目,通过浏览器访问扣减库存接口,库存由10000正常扣减为9999
⑦将扣减的库存恢复为10000,使用jmeter压测工具并发测试。添加一个线程组:并发100循环100次,即10000次请求
⑧给线程组添加HTTP Request请求
⑨添加扣减库存接口配置
⑩添加压测报告
⑪开始使用jmeter压测
测试结果:请求总数10000次,平均请求时间130ms,中位数(50%)请求是在83ms内完成的,错误率0%,每秒钟平均吞吐量754次。库存还没有扣减的为9808。此时就产生了超卖现象。实际库存应该为0。
⑫对于该并发问题我们使用jvm的synchronized本地加锁或者使用ReentrantLock加锁
⑬ 恢复库存数据为10000,再次使用jmeter工具压测
测试结果:请求总数10000次,平均请求时间332ms,中位数(50%)请求是在304ms内完成的,错误率0%,每秒钟平均吞吐量296次。库存扣减为0。本地加锁确实解决了并发的问题。可以从测试结果中发现,本地加锁确实可以解决“超卖的问题”,同时也会降低接口访问请求的吞吐量,由于加锁的原因。
⑭本地加锁存在的问题
对于单应用而言,本地加锁确实能够解决并发访问的问题。我们知道本地锁只能对本jvm虚拟机内是有效的,对于分布式多服务应用而言,为了高可用高并发往往会将单个应用部署为多个服务节点,这个时候,本地锁只能对本服务有效,依然会有并发的问题。那么对于多服务的应用该如何解决并发问题呢,这就要用到分布式锁了,关于分布式锁的相关内容实战,请关注我的后期的内容。
结语
对于“超卖”这一类问题,我们只能通过加锁的方式控制并发访问。在实际开发过程中,对于单体应用场景而言,我们可以使用本地锁实现并发访问控制,对于分布式微服务应用的场景中,我们可以使用分布式锁,控制并发访问。在后续的内容中,我们通过具体的技术解决方案,实现并发问题的解决。
这篇关于(一)库存超卖案例实战——库存超卖现象的产生及其解决方案概述的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!