1.启用事务
批量写入场景里要不要启用事物,其实很多人都有自己的看法,这里我给出启用于不启用的利弊,
- 启用事务:好处在于如批量插入过程中,异常情况可以保证原子性,但是性能比不开事务低,在特大数据量下会明显低一个档次
- 不启用事务:好处就是写入性能高,特大数据量写入性能提升明显,但是无法保证原子性
假如在批量写入过程中发生网络波动或者数据库宕机,我们其实只需要重新新建一条通知消息,然后重新操作即可。
因为上一条通知消息因为批量插入步骤没有全部完成,所以推送状态是失败。后续等开发人员处理一下脏数据即可。
2.大事务
@Transactional
是 Spring
框架提供得事务注解,相信这是许多人都知道的,但是在一些高性能场景下,是不建议使用的,推荐通过编程式事务来手动控制事务提交或者回滚,减少事务影响范围,因而提升性能。
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);
}
可以看到上面订单回滚的代码逻辑有四个步骤,如下,
- 查询订单是否存在
- 更新订单为已取消状态
- 订单商品数量增加
- 返还用户优惠券
这里面有个问题,订单回滚方法里面其实只有 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);
}
}
可以看到采用编程式事务后,我们将查询逻辑排除在事务之外,这样也就减小了事物影响范围。