Jmeter多线程下MySQL死锁的问题

来源:-

chorifa

2019-04-16 16:57

对创建订单(秒杀接口)进行压测(现在只开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;
}


写回答 关注

1回答

  • chorifa
    2019-04-21 11:10:28

    原因是连接池配置的不够多,Transaction事务中由于新开了一个生成订单号的Transaction,如果进来的线程用尽了连接数,新开的订单号的Transaction得不到有效连接(外部的Transaction会被挂起),导致所有的Transaction都走不下去,最后MySQL超时。

    tomcat默认的线程数为1000,所以配置的连接数大于1000就没有问题了。

SpringBoot构建电商基础秒杀项目

应用SpringBoot快速搭建拥有用户、商品、交易及秒杀活动的电商秒杀应用。

49064 学习 · 954 问题

查看课程

相似问题