思路
业务代码
@Override
@Transactional
public Result seckillVoucher(Long voucherId) {
//查询优惠卷
SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId);
//判断秒杀是否开始
if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
//秒杀尚未开始
return Result.fail("秒杀尚未开始,请耐心等待");
}
//判断秒杀是否结束
if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
//秒杀已经结束
return Result.fail("本次秒杀已经结束");
}
//秒杀处于正常时间段
//判断库存是否充足
if (voucher.getStock()<1) {
//库存不足
return Result.fail("本次秒杀已经被抢完");
}
//库存充足
//扣减库存
boolean update = iSeckillVoucherService.update().setSql("stock = stock - 1").eq("voucher_id", voucherId).update();
//判断库存扣减是否成功
if (!update) {
//库存扣减失败,返回信息
return Result.fail("库存不足");
}
//库存扣减成功,添加订单信息
VoucherOrder voucherOrder = new VoucherOrder();
//添加订单id
//使用订单生成器生成id
long id = redisIdWorker.nextId("order");
voucherOrder.setId(id);
//添加用户id
//从拦截器中获取
Long userId = UserHolder.getUser().getId();
voucherOrder.setUserId(userId);
//添加消费卷id
voucherOrder.setVoucherId(voucherId);
//添加到数据库
boolean save = this.save(voucherOrder);
if (!save){
//添加失败
return Result.fail("下单失败");
}
return Result.ok(id);
}
出现的问题,多线程并发冲突,会导致多卖
引入概念乐观锁悲观锁,悲观锁为强制所有线程串行执行,会导致效率低下
乐观锁
1.版本号法
2.CAS法
利用CAS后代码
@Override
@Transactional
public Result seckillVoucher(Long voucherId) {
//查询优惠卷
SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId);
//判断秒杀是否开始
if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
//秒杀尚未开始
return Result.fail("秒杀尚未开始,请耐心等待");
}
//判断秒杀是否结束
if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
//秒杀已经结束
return Result.fail("本次秒杀已经结束");
}
//秒杀处于正常时间段
//判断库存是否充足
if (voucher.getStock()<1) {
//库存不足
return Result.fail("本次秒杀已经被抢完");
}
//库存充足
//扣减库存
boolean update = iSeckillVoucherService.update().setSql("stock = stock - 1")
.eq("voucher_id", voucherId)
.eq("stock",voucher.getStock())
.update();
//判断库存扣减是否成功
if (!update) {
//库存扣减失败,返回信息
return Result.fail("库存不足");
}
//库存扣减成功,添加订单信息
VoucherOrder voucherOrder = new VoucherOrder();
//添加订单id
//使用订单生成器生成id
long id = redisIdWorker.nextId("order");
voucherOrder.setId(id);
//添加用户id
//从拦截器中获取
Long userId = UserHolder.getUser().getId();
voucherOrder.setUserId(userId);
//添加消费卷id
voucherOrder.setVoucherId(voucherId);
//添加到数据库
boolean save = this.save(voucherOrder);
if (!save){
//添加失败
return Result.fail("下单失败");
}
return Result.ok(id);
}
改进后发现成功率大幅下降预期成功率在0.5实际成功率0.2(乐观锁的弊端)
改进方法不在进行判断数量是否与开始查询到的数量一致,改进行判断数量是否大于0
代码
@Override
@Transactional
public Result seckillVoucher(Long voucherId) {
//查询优惠卷
SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId);
//判断秒杀是否开始
if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
//秒杀尚未开始
return Result.fail("秒杀尚未开始,请耐心等待");
}
//判断秒杀是否结束
if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
//秒杀已经结束
return Result.fail("本次秒杀已经结束");
}
//秒杀处于正常时间段
//判断库存是否充足
if (voucher.getStock()<1) {
//库存不足
return Result.fail("本次秒杀已经被抢完");
}
//库存充足
//扣减库存
boolean update = iSeckillVoucherService.update().setSql("stock = stock - 1")
.eq("voucher_id", voucherId)
.gt("stock",0)
.update();
//判断库存扣减是否成功
if (!update) {
//库存扣减失败,返回信息
return Result.fail("库存不足");
}
//库存扣减成功,添加订单信息
VoucherOrder voucherOrder = new VoucherOrder();
//添加订单id
//使用订单生成器生成id
long id = redisIdWorker.nextId("order");
voucherOrder.setId(id);
//添加用户id
//从拦截器中获取
Long userId = UserHolder.getUser().getId();
voucherOrder.setUserId(userId);
//添加消费卷id
voucherOrder.setVoucherId(voucherId);
//添加到数据库
boolean save = this.save(voucherOrder);
if (!save){
//添加失败
return Result.fail("下单失败");
}
return Result.ok(id);
}
测试(与预期结果一致)