数据库事务使用

本文讨论了在批量写入场景中启用事务的优点(保证原子性)和缺点(性能损失),以及大事务情况下使用编程式事务以减少影响范围并提高性能。作者给出了使用@Transactional注解和编程式事务的例子进行对比。
摘要由CSDN通过智能技术生成

1.启用事务

批量写入场景里要不要启用事物,其实很多人都有自己的看法,这里我给出启用于不启用的利弊,

  • 启用事务:好处在于如批量插入过程中,异常情况可以保证原子性,但是性能比不开事务低,在特大数据量下会明显低一个档次
  • 不启用事务:好处就是写入性能高,特大数据量写入性能提升明显,但是无法保证原子性

假如在批量写入过程中发生网络波动或者数据库宕机,我们其实只需要重新新建一条通知消息,然后重新操作即可。

因为上一条通知消息因为批量插入步骤没有全部完成,所以推送状态是失败。后续等开发人员处理一下脏数据即可。

2.大事务

@TransactionalSpring 框架提供得事务注解,相信这是许多人都知道的,但是在一些高性能场景下,是不建议使用的,推荐通过编程式事务来手动控制事务提交或者回滚,减少事务影响范围,因而提升性能。

2.1使用事务注解

采用 @Transactional 事务注解

@Transactional(rollbackFor = Exception.class)
public void doUnPaidTask(Long orderId) {
    // 1. 查询订单是否存在
    Order order = orderService.getById(orderId);
    ,,,

    // 2. 更新订单为已取消状态
    order.setOrderStatus((byte) OrderStatusEnum.ORDER_CLOSED_BY_EXPIRED.getOrderStatus());
    orderService.updateById(order);
    ...
    // 3. 订单商品数量增加
    LambdaQueryWrapper<OrderItem> queryWrapper = Wrappers.lambdaQuery();
    queryWrapper.eq(OrderItem::getOrderId, orderId);
    List<OrderItem> orderItems = orderItemService.list(queryWrapper);
    for (OrderItem orderItem : orderItems) {
        Long goodsId = orderItem.getGoodsId();
        Integer goodsCount = orderItem.getGoodsCount();
        if (!goodsDao.addStock(goodsId, goodsCount)) {
            throw new BusinessException("秒杀商品货品库存增加失败");
        }
    }

    // 4. 返还用户优惠券
    couponService.releaseCoupon(orderId);
    log.info("---------------订单orderId:{},未支付超时取消成功", orderId);
}

可以看到上面订单回滚的代码逻辑有四个步骤,如下,

  1. 查询订单是否存在
  2. 更新订单为已取消状态
  3. 订单商品数量增加
  4. 返还用户优惠券

这里面有个问题,订单回滚方法里面其实只有 2、3、4 步骤是需要在一个事物里执行的,第 1 步其实可以放在事物外面来执行,以此缩小事物范围。

2.2使用编程式事务

使用编程式事务对其优化后,代码如下

@Resource
private PlatformTransactionManager platformTransactionManager;
@Resource
private TransactionDefinition transactionDefinition;

public void doUnPaidTask(Long orderId) {
    // 启用编程式事务
    // 1. 在开启事务钱查询订单是否存在
    Order order = orderService.getById(orderId);
    ...
    // 2. 开启事务
    TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
    try {
        // 3. 设置订单为已取消状态
        order.setOrderStatus((byte) OrderStatusEnum.ORDER_CLOSED_BY_EXPIRED.getOrderStatus());
        orderService.updateById(order);
        ...
        // 4. 商品货品数量增加
        LambdaQueryWrapper<OrderItem> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(OrderItem::getOrderId, orderId);
        List<OrderItem> orderItems = orderItemService.list(queryWrapper);
        for (OrderItem orderItem : orderItems) {
            Long goodsId = orderItem.getGoodsId();
            Integer goodsCount = orderItem.getGoodsCount();
            if (!goodsDao.addStock(goodsId, goodsCount)) {
                throw new BusinessException("秒杀商品货品库存增加失败");
            }
        }

        // 5. 返还优惠券
        couponService.releaseCoupon(orderId);
        // 6. 所有更新操作完成后,提交事务
        platformTransactionManager.commit(transaction);
        log.info("---------------订单orderId:{},未支付超时取消成功", orderId);
    } catch (Exception e) {
        log.info("---------------订单orderId:{},未支付超时取消失败", orderId, e);
        // 7. 发生异常,回滚事务
        platformTransactionManager.rollback(transaction);
    }
}

可以看到采用编程式事务后,我们将查询逻辑排除在事务之外,这样也就减小了事物影响范围。

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值