package com.yiridancan.reduceInventory.service.impl;
import com.yiridancan.reduceInventory.entity.Goods;
import com.yiridancan.reduceInventory.mapper.GoodsMapper;
import com.yiridancan.reduceInventory.service.IGoodsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 商品实现类
* @author yiridancan
* @date 2024/3/23 18:35
*/
@Slf4j
@Service
public class GoodsServiceImpl implements IGoodsService {
@Autowired
private GoodsMapper goodsMapper;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 扣减库存
* @param goodsId 商品id
* @author yiridancan
* @date 2024/3/23 18:33
*/
@Override
public void reduceInventory(int goodsId) {
//1.根据商品id获取商品库存数量
Goods goods = goodsMapper.findGoodsInventory(goodsId);
if(Objects.isNull(goods)){
log.error("未获取到商品信息");
return;
}
//2.如果库存数量大于0则扣减库存,如果等于0代表没有货物打印错误信息
if(goods.getInventoryCount() > 0 ){
//默认扣减库存1
goods.setInventoryCount(goods.getInventoryCount()-1);
goodsMapper.updateGoodsInventory(goods);
log.info("{}扣减库存成功,扣减后库存为:{}",goods.getName(),goods.getInventoryCount());
}else {
log.error("{}库存为0",goods.getName());
}
}
/**
* 通过乐观锁实现扣减库存
* @param goodsId 商品id
* @author yiridancan
* @date 2024/3/25 22:33
*/
@Override
public void casReduceInventory(int goodsId) {
int retryCount = 0;
//重试次数设置为3,避免无休止的重试占用紫鸢
while (retryCount <=3){
//1.根据商品id获取商品信息
Goods goods = goodsMapper.findGoodsInventory(goodsId);
if(Objects.isNull(goods) || goods.getInventoryCount() == 0){
log.error("未获取到商品信息或库存数量不足");
return;
}
//默认扣减库存1
goods.setInventoryCount(goods.getInventoryCount()-1);
int updateRow = goodsMapper.updateGoodsInventoryByCAS(goods);
//如果修改条数大于0代表扣减库存成功
if(updateRow > 0 ){
log.info("{}扣减库存成功,扣减后库存为:{}",goods.getName(),goods.getInventoryCount());
return;
}
retryCount++;
log.error("{}商品被修改过,进行重试!!版本号:{}",goods.getName(),goods.getDataVersion());
}
}
/**
* 通过Redis扣减库存
*
* @param goodsId 商品id
* @author yiridancan
* @date 2024/3/27 15:48
*/
@Override
public void redisReduceInventory(int goodsId) {
String prefix = "goodsInventory:";
//将商品数据缓存到Redis中,key是商品id,value是商品库存数量
goodsMapper.findGoodsAll().forEach(goods -> {
stringRedisTemplate.opsForValue().set(prefix+goods.getId(),String.valueOf(goods.getInventoryCount()));
});
//lua脚本,一般放在文件中
String script = "local key = KEYS[1] -- 商品的键名\n" +
"local amount = tonumber(ARGV[1]) -- 扣减的数量\n" +
"\n" +
"-- 获取商品当前的库存量\n" +
"local stock = tonumber(redis.call('get', key))\n" +
"\n" +
"-- 如果库存足够,则减少库存并返回新的库存量\n" +
"if stock >= amount then\n" +
" redis.call('decrby', key, amount)\n" +
" return redis.call('get', key)\n" +
"else\n" +
" return \"INSUFFICIENT STOCK\"\n" +
"end\n";
DefaultRedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
// 创建一个包含库存key的列表
List<String> keys = Collections.singletonList(prefix + goodsId);
// 创建一个包含扣减数量的参数列表
List<String> args = Collections.singletonList(Integer.toString(1));
// 执行Lua脚本,传入键列表和参数列表
String result = stringRedisTemplate.execute(redisScript, keys, args.toArray(new String[0]));
//如果不是库存不足代表扣减成功
if(!result.equals("INSUFFICIENT STOCK")){
log.info("扣减库存成功,库存数量:{}",result);
}else {
log.error("库存数量不足");
}
}
}
没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
在高并发电商场景下,商品超卖(即销售量超出库存)是常见问题,主要由并发扣减库存导致。常规做法是在扣减库存前检查库存充足性,但面对大量并发请求时,这种方法可能失效。为此,可采用以下几种策略: 悲观锁:在数据库层面,对库存记录进行锁定,确保同一时刻只有一个事务能扣减库存,但可能导致锁竞争激烈,影响性能。 乐观锁:通过在库存记录中增加版本号字段,更新时验证版本号是否改变,若改变则表示库存已被其他事务修改,避免了长时间锁等待,但需合理设计重试策略。 Redis:利用Redis的高速与原子操作特性,将库存存入Redis中,通过DECR等命令原子地扣减库存,有效防止超卖。同时,Redis支持lua脚本,实现复杂逻辑的原子执行。 实际应用中,可根据业务需求和技术栈选择合适方案,如结合Redis作为库存中间件,辅以分布式锁策略防止集群环境下超卖。此外,还可借助消息队列进行削峰填谷,确保系统稳定可靠。总的来说,解决商品超卖问题需要综合运用多种技术和策略,以适应复杂的高并发场景。
资源推荐
资源详情
资源评论
收起资源包目录
reduceInventory.zip (29个子文件)
reduceInventory
mvnw.cmd 7KB
pom.xml 2KB
src
test
java
com
yiridancan
reduceInventory
ReduceInventoryApplicationTests.java 4KB
main
resources
mapper
Goods.xml 1023B
application.yml 482B
java
com
yiridancan
reduceInventory
ReduceInventoryApplication.java 341B
mapper
GoodsMapper.java 895B
controller
GoodsController.java 743B
service
impl
GoodsServiceImpl.java 5KB
IGoodsService.java 718B
entity
Goods.java 1016B
.idea
jarRepositories.xml 871B
uiDesigner.xml 9KB
workspace.xml 7KB
misc.xml 611B
compiler.xml 791B
.gitignore 184B
encodings.xml 191B
target
classes
mapper
Goods.xml 1023B
application.yml 482B
com
yiridancan
reduceInventory
ReduceInventoryApplication.class 794B
mapper
GoodsMapper.class 722B
controller
GoodsController.class 1KB
service
IGoodsService.class 301B
impl
GoodsServiceImpl.class 6KB
entity
Goods.class 1KB
test-classes
com
yiridancan
reduceInventory
ReduceInventoryApplicationTests.class 6KB
generated-test-sources
test-annotations
generated-sources
annotations
mvnw 11KB
.gitignore 395B
共 29 条
- 1
资源评论
yiridancan
- 粉丝: 325
- 资源: 2
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功