文章目录业务分析问题1高并发下出现超售问题2一个用户抢购多张1使用同步锁 synchronized2事务失效最终代码结构业务分析秒杀业务的核心难点在于应对高并发带来的数据一致性问题主要聚焦两个矛盾限定数量防止超售库存扣减需要保证原子性避免并发下卖出超过实际库存。一人一单同一个用户 ID 只能抢购一次保证公平。问题1高并发下出现超售多个线程同时读取库存为 1都判断“充足”然后各自扣减结果库存变成负数。解决方案CAS 乐观锁在更新库存时不依赖锁而是增加一个版本条件仅当库存大于 0 时才扣减。booleansuccessseckillVoucherService.update().setSql(stock stock - 1).eq(voucher_id,voucherId).gt(stock,0)// CAS 条件库存 0.update();问题2一个用户抢购多张1使用同步锁 synchronized给“查询是否已购买”和“创建订单”整个逻辑加锁。synchronized(userId.toString().intern()){returncreateVoucherOrder(voucherId);}所有用户争夺同一把锁锁粒度太粗。→ 改为 只对当前用户 ID 加锁即 synchronized (userId.toString().intern())。intern() 保证相同字符串对象唯一避免不同线程使用不同 String 对象导致锁失效。2事务失效上述代码看似可行但Transactional 会失效原因是 Spring AOP 的代理机制。createVoucherOrder 方法标记了 Transactional期望在事务中执行。然而我们通过 this.createVoucherOrder 直接调用跳过了 Spring 生成的代理对象。Spring 的事务管理基于 AOP 动态代理JDK/CGLIB。容器注入的是代理对象代理对象会在调用目标方法前后处理事务开启、提交/回滚。如果我们拿到的是原始对象this并直接调用代理根本没机会插手事务注解自然失效。解决方案暴露代理对象在主方法中通过 AopContext.currentProxy() 获取当前类的代理对象。通过代理对象调用 createVoucherOrder。必须先在启动类或配置上启用 EnableAspectJAutoProxy(exposeProxy true)允许暴露代理。// 主方法publicResultseckillVoucher(LongvoucherId){// ...synchronized(userId.toString().intern()){IVoucherOrderServiceproxy(IVoucherOrderService)AopContext.currentProxy();returnproxy.createVoucherOrder(voucherId);}}最终代码结构OverridepublicResultseckillVoucher(LongvoucherId){//查询优惠券SeckillVouchervoucherseckillVoucherService.getById(voucherId);//判断秒杀是否开始if(voucher.getBeginTime().isAfter(LocalDateTime.now())){//秒杀尚未开始returnResult.fail(秒杀尚未开始);}if(voucher.getEndTime().isBefore(LocalDateTime.now())){//秒杀已经结束returnResult.fail(秒杀已经结束);}//判断库存是否充足if(voucher.getStock()1){//库存不足returnResult.fail(库存不足);}LonguserIdUserHolder.getUser().getId();//仅限单体应用使用synchronized(userId.toString().intern()){//实现获取代理对象 比较复杂IVoucherOrderServiceproxy(IVoucherOrderService)AopContext.currentProxy();returncreateVoucherOrder(voucherId);}TransactionalpublicResultcreateVoucherOrder(LongvoucherId){// 5. 一人一单LonguserIdUserHolder.getUser().getId();// 5.1. 查询订单intcountquery().eq(user_id,userId).eq(voucher_id,voucherId).count();// 5.2. 判断是否存在if(count0){// 用户已经购买过了returnResult.fail(用户已经购买过一次);}// 6. 扣减库存booleansuccessseckillVoucherService.update().setSql(stock stock - 1)// set stock stock - 1.eq(voucher_id,voucherId).gt(stock,0)// where id ? and stock 0.update();if(!success){// 扣减失败returnResult.fail(库存不足);}// 7. 创建订单VoucherOrdervoucherOrdernewVoucherOrder();// 7.1. 订单idlongorderIdredisIdWorker.nextId(order);voucherOrder.setId(orderId);// 7.2. 用户idvoucherOrder.setUserId(userId);// 7.3. 代金券idvoucherOrder.setVoucherId(voucherId);save(voucherOrder);// 7. 返回订单idreturnResult.ok(orderId);}