对创建订单(秒杀接口)进行压测(现在只开10个线程循环1次),会报如下错误 The error occurred while setting parameters### SQL: update item_stock set stock = stock - ? where stock >= ? and item_id = ?### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction; Lock wait timeout exceeded; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction]2019-04-16 16:45:17.323 WARN 11904 --- [nio-8080-exec-7] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.dao.CannotAcquireLockException: 已经对相应字段添加过索引了,不能解决问题。即使仅保留 减库存 生成订单号 订单入库 三个操作仍旧是会出现上边的问题。 public OrderModel createOrder(Integer userId, Integer itemId, Integer promoId, Integer amount) throws BusinessException { // 校验状态:用户是否存在,商品是否存在,数量是否合法,活动是否合法 // 在getItemById方法中创建itemModel时就已经判断了服务器时间活动状态 ItemModel itemModel = itemService.getItemById(itemId); if(itemModel == null) throw new BusinessException(EmBusinessError.ITEM_NOT_EXIST); UserModel userModel = userService.getUserById(userId); if(userModel == null) throw new BusinessException(EmBusinessError.USER_NOT_EXIST); if(amount <=0 || amount >99) throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"购买数量过大"); if(promoId != null){ // 非空,比较是否同一个活动 if(!promoId.equals(itemModel.getPromoModel().getId())) throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"不存在该活动"); // 活动是否在进行中 else if(itemModel.getPromoModel().getStatus() != 2) throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"活动不在进行中"); } // 减库存的两种方式:下单减库存和支付减库存 // 下单减库存是指,当提交订单时查询是否有剩余可买的商品,如果有就将商品“加锁”(数量减一),并完成下单,再去支付 // 支付减库存是指,当提交订单时仅仅查询是否有剩余可买的商品,不对其加锁,直到支付完成才减库存。 // 下单减库存能确保下单后一定能获得商品。支付减库存在并发下存在超卖商品风险 // 某些商家库存120件,表明的是100件,采用下单减库存可以承受20件的超卖风险,同时能营造出 火爆和售罄的紧张感 // 采用下单减库存方式 boolean isDecreased = itemService.decreaseStock(itemId,amount); if(!isDecreased) throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH); // 订单入库 OrderModel orderModel = new OrderModel(); orderModel.setUserId(userId); orderModel.setItemId(itemId); orderModel.setAmount(amount); orderModel.setPromoId(promoId); if(promoId == null) orderModel.setItemPrice(itemModel.getPrice()); else orderModel.setItemPrice(itemModel.getPromoModel().getPromoItemPrice()); orderModel.setOrderPrice(orderModel.getItemPrice().multiply(BigDecimal.valueOf(amount))); // 生成交易流水号 orderModel.setId(sequenceService.generateOrderId()); OrderDataObject orderDataObject = convertOrderDataFromOrderModel(orderModel); orderDataObjectMapper.insertSelective(orderDataObject); // 销量增加,暂时不考虑支付金额的情况 itemService.increaseSales(itemId,amount); // 返回Controller return orderModel; }
原因是连接池配置的不够多,Transaction事务中由于新开了一个生成订单号的Transaction,如果进来的线程用尽了连接数,新开的订单号的Transaction得不到有效连接(外部的Transaction会被挂起),导致所有的Transaction都走不下去,最后MySQL超时。
tomcat默认的线程数为1000,所以配置的连接数大于1000就没有问题了。