关于一个秒杀业务的思考

一个秒杀的业务如下:

public Result execMiaoSha(Long goodsId,User user){
    //判断user是否秒杀过goodsId的商品
    if(redisDao.get(Prefix.goodsAndUser+goodsId+user.getId)!=null){
        throw GlobalException("不可以重复秒杀");
    }

	//获取商品信息
    Goods goods = redisDao.get(Prefix.goods+goodsId);//获取json串,通过fastJson转化为一个Goods对象
    if(goods == null){
        goods = goodsDao.getGoodsById(goodsId);
        if(goods == null){
            throw GlobalException("商品不存在");
        }
    }

    //TO执行:1.更新goods数据 2.插入order数据
    goods.setGoodsStock(goods.stock-1);
    Order order = getOderByUserAndGoods(goods,user);

    goodsDao.updateGoods(goods);
    orderDao.insert(order);
    
    //信息缓存在redis
    redisDao.set(Prefix.goods+goodsId,bean2Json(goods));
    redisDao.set(Prefix.goodsAndUser+goodsId+user.getId,order);
    
    return Result.success("秒杀成功");
}

本文针对这四行核心代码做出优化:

	//TO执行:1.更新goods数据 2.插入order数据
    goods.setGoodsStock(goods.stock-1);
    Order order = getOderByUserAndGoods(goods,user);

    goodsDao.updateGoods(goods);
    orderDao.insert(order);

原始方案:将这四行代码放入一个方法中,并且声明成为一个事务。

	@Transactional
    public void execDB(){
        try {
            //TO执行:1.更新goods数据 2.插入order数据
            goods.setGoodsStock(goods.stock - 1);
            Order order = getOderByUserAndGoods(goods, user);

            goodsDao.updateGoods(goods);
            orderDao.insert(order);
        }catch (Exception e){
            throw GlobalException("秒杀数据失败");
        }
    }

优化一:更换语序
首先是在更新操作的时候给行加锁,插入并不会加锁,如果更新操作在前,那么就需要执行完更新和插入以后事务提交或回滚才释放锁。而如果插入在前,更新在后,那么只有在更新时才会加行锁,之后在更新完以后事务提交或回滚释放锁。

	@Transactional
    public void execDB(){
        try {
            //TO执行:1.更新goods数据 2.插入order数据
            goods.setGoodsStock(goods.stock - 1);
            Order order = getOderByUserAndGoods(goods, user);

			orderDao.insert(order);
            goodsDao.updateGoods(goods);
        }catch (Exception e){
            throw GlobalException("秒杀数据失败");
        }
    }

优化二:存储过程
优点:用存储过程这两个操作,一次网络io,执行两天命令。
缺点:数据库提供业务运算,数据库开销变大

优化三:
可以换一种思路:miaosha是一个拼接的sql,相当于一个请求执行两天sql语句,一定层度上将逻辑相关的代码交给spring,mysql只处理sql(具体做法可以百度:mybatis一次请求执行多条语句)

@Transactional
public void execDB(){
    try {
        //TO执行:1.更新goods数据 2.插入order数据
        goods.setGoodsStock(goods.stock - 1);
        Order order = getOderByUserAndGoods(goods, user);

		orderGoodsDao.miaosha(order,goods);
    }catch (Exception e){
        throw GlobalException("秒杀数据失败");
    }
}

优化四(kafka解耦):
用一个消息中间件解耦,下单以后发送一个,然后写一个kafkaListen监听数据接收,进行秒杀,调用orderGoodsDao.miaosha(order,goods)。

@Transactional
public void execDB(){
    try {
        //TO执行:1.更新goods数据 2.插入order数据
        goods.setGoodsStock(goods.stock - 1);
        Order order = getOderByUserAndGoods(goods, user);
		kafkaServer.send(Json.fromBean2Json(order)+":"+Json.fromBean2Json(goods.getId()));
		
    }catch (Exception e){
        throw GlobalException("秒杀数据失败");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值