秒杀系统的接口优化

该博客探讨了如何优化秒杀系统,通过Redis预减库存降低数据库访问压力,利用RabbitMQ异步处理下单,以及采用内存标记策略,当库存耗尽后快速响应后续请求,减轻系统负载。详细介绍了系统初始化、请求处理流程以及内存标记优化等关键步骤。
摘要由CSDN通过智能技术生成

思路:减少数据库访问
1、系统初始化,把商品库存加载到redis
2、收到请求在redis预减库存,库存不足,直接返回,
3. 可以在redis里面做内存标记(这一步是很大的优化,只要库存减成零,后面的请求无论是100个还是一万个都是直接失败,压力很小),否则进入3 下一步
4、异步下单:如果有库存,不是直接连接数据库写入,而是对RabbitMQ操作,请求入队,立即返回排队中,类似我们在12306买火车票,并不会马上返回下单成功或者失败,而是显示排队中
5、客户端正常做轮询,判断是否秒杀成功。 服务端在入队之后,会请求出队,生成订单 减少库存

实现方法:

redis预减库存

系统初始化的时候,查询商品数量,把商品库存数量加载到redis缓存里面,

/**
	 * 系统初始化
	 * */
	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());
			localOverMap.put(goods.getId(), false);
		}
	}

执行秒杀的时候,不是直接在数据库中减库存,为了提高并发能力,在redis缓存里预减库存,如果库存不足,就返回指定的错误,如果库存充足,执行预减库存操作。
根据user和goodsid到redis里面判断订单是否已经存在,判断是否重复秒杀,如果重复了就返回不能能重复秒杀。

         //预减库存
    	long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, ""+goodsId);//10
    	if(stock < 0) {
    		 localOverMap.put(goodsId, true);
    		return Result.error(CodeMsg.MIAO_SHA_OVER);
    	}
    	//判断是否已经秒杀到了
    	MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
    	if(order != null) {
    		return Result.error(CodeMsg.REPEATE_MIAOSHA);
    	}

RabbitMQ消息队列把下单异步化

如果没有,就执行入队操作,把user和goodsid信息发送出去。调用rabbitmq的sender方法,rabbitmq使用默认的direct模式。然后返回一个状态,表示排队中。

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

秒杀到之后,发送消息,在接收端MqReciiver里面,判断库存,判断如果不是重复秒杀,就调用MiaoshaService里面的方法,往下对数据库进行 减库存,下订单,写入秒杀订单。

@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;
	    	}
	    	//减库存 下订单 写入秒杀订单
	    	miaoshaService.miaosha(user, goods);
		}

MiaoshaService里面,减库存,下订单,写入秒杀订单是一个事务操作,要么全部成功,要么全部失败,所以需要给方法添加@Transactional注解。 数据库写入订单之后会把订单信息写入redis里面来。

@Transactional
	public OrderInfo miaosha(MiaoshaUser user, GoodsVo goods) {
		//减库存 下订单 写入秒杀订单
		boolean success = goodsService.reduceStock(goods);
		if(success) {
			//order_info maiosha_order
			return orderService.createOrder(user, goods);
		}else {
			setGoodsOver(goods.getId());
			return null;
		}
	}

在页面里做轮询,查询秒杀结果,如果秒杀失败,直接返回失败。如果成功,就接着查询是否下单成功,如果没成功就接着轮询,如果成功,点击查看订单,就返回订单信息。

内存标记优化:

在MIaoshaController里吗,新建一个HashMap,把商品id和标记值初始化为false存进去。
在预减库存的时候,如果商品库存小于零,做一个标记true,后续的请求不再去访问redis,直接返回失败,好处是可以减少系统开销。

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());
			localOverMap.put(goods.getId(), false);
		}
	}
//内存标记,减少redis访问
    	boolean over = localOverMap.get(goodsId);
    	if(over) {
    		return Result.error(CodeMsg.MIAO_SHA_OVER);
    	}
    	//预减库存
    	long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, ""+goodsId);//10
    	if(stock < 0) {
    		 localOverMap.put(goodsId, true);
    		return Result.error(CodeMsg.MIAO_SHA_OVER);
    	}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值