目录
点击商品秒杀时的(主要是减少数据库层面的访问)
一步一步优化
最初
判断数据库内存是否足够 足够再判断是否已经在订单表中存在(存在表示已经秒杀到了) 然后再进行订单创建以及库存的降低(这一步需要事务)
优化1
判断数据库内存是否足够 足够再判断redis中是否存在把用户ID和商品ID作为key,订单信息为value的值(存在表示秒已经杀到了直接返回在Redis获取的订单信息) 然后再进行订单创建以及库存的降低和把用户ID和商品ID作为key放入redis(这一步需要事务)
优化2
把商品数量放到redis中
①:在初始化项目时,把商品数量放到Redis中
public class MiaoshaController implements InitializingBean {
@Autowired RedisService redisService;
private HashMap<Long, Boolean> localOverMap = new HashMap<Long, Boolean>();
/** * 系统初始化 * */ public void afterPropertiesSet() throws Exception { //数据库获取所有需要秒杀商品及其数量 List<GoodsVo> goodsList = goodsService.listGoodsVo(); if(goodsList == null) { return; } for(GoodsVo goods : goodsList) { redisService.set(GoodsKey.getMiaoshaGoodsStock,""+goods.getId(), goods.getStockCount()); } } |
②:判断Redis中商品数量是否足够 足够再判断redis中是否存在把用户ID和商品ID作为key,订单信息为value的值(存在表示秒已经杀到了直接返回在Redis获取的订单信息) 然后再进行订单创建以及库存的降低和把用户ID和商品ID作为key放入redis(这一步需要事务)
优化3(最终版本)
①:项目启动把商品库存数量放入redis中
②:收到请求,redis库存预减(使用内存标记可以降低redis访问),库存不足直接返回,库存足,进入第③步
private HashMap<Long, Boolean> localOverMap = new HashMap<Long, Boolean>();
1 在第①步直接设置 每个商品内存标记 false表示还有商品数量 for(GoodsVo goods : goodsList) { redisService.set(GoodsKey.getMiaoshaGoodsStock, ""+goods.getId(), goods.getStockCount()); localOverMap.put(goods.getId(), false); }
2 预减redis库存时,如果没有商品数量 设置为true //预减库存 long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, ""+goodsId);//10 if(stock < 0) { localOverMap.put(goodsId, true); return Result.error(CodeMsg.MIAO_SHA_OVER); } |
③:请求入队列,立即返回排队中
④:请求出队,减少库存生成订单(rabbitMQ Direct模式)
Sender:
public void sendMiaoshaMessage(MiaoshaMessage mm) { String msg = RedisService.beanToString(mm); log.info("send message:"+msg); amqpTemplate.convertAndSend(MQConfig.MIAOSHA_QUEUE, msg); } |
消费者:
@RabbitListener(queues=MQConfig.MIAOSHA_QUEUE) public void receive(String message) { log.info("receive message:"+message); MiaoshaMessage mm=RedisService.stringToBean(message, MiaoshaMessage.class); MiaoshaUser user = mm.getUser(); long goodsId = mm.getGoodsId();
GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId); int stock = goods.getStockCount(); if(stock <= 0) { return; } //判断是否已经秒杀到了 MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId); if(order != null) { return; } //减库存 下订单 写入秒杀订单 处理完毕在redis中放入一个标识(是否成功或者失败) miaoshaService.miaosha(user, goods); } |
⑤:客户端轮训,判断是否秒杀成功
第④步进入队列后返回固定的码:0
0: 排队中
orderId:秒杀成功
-1 :秒杀失败
轮训时需要新增加一个查询接口,查询redis 中当前商品+用户构成的key的value(也就是固定的码)。
回仓:30分钟不支付订单自动取消
30分钟以后,扫描下秒杀订单,看有哪些还没支付,把订单状态置为已经取消,可秒杀数量+1,redis的秒杀结束的标志位还原,内存的数量还原