库存售不完的BUG

缓存库存中的问题

库存售不完的BUG
我们之前使用map来标记我们的库存已售完,比如当我们库存有100个,已经售完99个,当最后一个请求过来,此时我们最后一个请求过来,然后redis库存预减成功,但是数据库报错, 事务虽然回滚,但是redis中就已经售完了,所以此时数据库和redis中的数据就不一致了,所以我们同时在处理异常时,也需要对我们redis缓存进行还原,并且如果我们redis中的库存已经为0了,我们的map缓存标记也将开启,那么后面的请求就全部都会被档回去了

所以,我们在查询订单不存在时,也就是我们下单失败时需要对缓存还原,

    		//当我们最后一个商品下单错误,数据库减少失败,redis库存恢复,但是这时又以及把库存标记了
			// 它就无法减我们的库存了,所以我们应该清空内存标记
			localOverMap.remove(goodsId);
    		//下单失败,数据减少失败,redis缓存还原,redis库存数加1
			redisService.incr(GoodsKey.getMiaoshaGoodsStock, ""+goodsId);

但是我这里只是处理了数据库报错的情况,但是如果我们同时发送了10000个请求,我们只有100个商品,前面一百个商品减成功,中间有报错的我们也还原了,但是redis中还是减少了10000个,但是我们还原缓存只是加1,那些正常请求并没有加1还原,所以我们在缓存中的库存还是负数,所以我们需要在库存判断也要加上还原

//这里判断不能小于等于,因为减去之后等于 说明还有是正常范围
			if(stock < 0) {
				localOverMap.put(goodsId, true);
				//还原库存
				redisService.incr(GoodsKey.getMiaoshaGoodsStock, ""+goodsId);
				//返回秒杀完毕
				return Result.error(CodeMsg.MIAO_SHA_OVER);
			}

然后完整代码

    @RequestMapping(value="/do_miaosha", method=RequestMethod.POST)
    @ResponseBody
    public Result<Integer> miaosha(HttpServletRequest request, HttpServletResponse response,
									 Model model, MiaoshaUser user,
									 @RequestParam("goodsId")long goodsId) {
    	model.addAttribute("user", user);
    	if(user == null) {
    		return Result.error(CodeMsg.SESSION_ERROR);
    	}
    	try {
			//内存标记,减少redis访问,从map中取出
			boolean over = localOverMap.get(goodsId);
			if(over) {
				return Result.error(CodeMsg.MIAO_SHA_OVER);
			}
			//预减库存  从缓存中减去库存
			//利用 redis 中的方法,减去库存,返回值为 减去1 之后的值
			long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, ""+goodsId);//10
			//这里判断不能小于等于,因为减去之后等于 说明还有是正常范围
			if(stock < 0) {
				localOverMap.put(goodsId, true);
				//还原库存
				redisService.incr(GoodsKey.getMiaoshaGoodsStock, ""+goodsId);
				//返回秒杀完毕
				return Result.error(CodeMsg.MIAO_SHA_OVER);
			}
			//判断是否已经秒杀到了
			MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
			if(order != null) {
				return Result.error(CodeMsg.REPEATE_MIAOSHA);
			}
		} catch (Exception e){
    		//当我们最后一个商品下单错误,数据库减少失败,redis库存恢复,但是这时又以及把库存标记了
			// 它就无法减我们的库存了,所以我们应该清空内存标记
			localOverMap.remove(goodsId);
    		//下单失败,数据减少失败,redis缓存还原,redis库存数加1
			redisService.incr(GoodsKey.getMiaoshaGoodsStock, ""+goodsId);
    		return  Result.error(CodeMsg.ORDER_NOT_ERROR);
		}

		//入队
		MiaoshaMessage mm = new MiaoshaMessage();
		mm.setUser(user);
		mm.setGoodsId(goodsId);
		sender.sendMiaoshaMessage(mm);
		return Result.success(0);//排队中
    }

分布式jvm缓存问题

当我们把这个处理方案使用在分布式项目时就会出现问题,我们需要解决我们jvm级别的缓存一致的问题,也就是我们map一致的问题
库存售不完的BUG
zookeeper可用解决缓存一致问题,后续更新

end…

栏目