大事务带来的问题及解决方案

一、问题描述

业务下单链路(大事务)整体耗时较高,影响整体接口性能

1、什么是大事务

运行时间长,长时间未提交的事务称之为大事务

2、大事务会带来那些问题

a、死锁

b、数据库连接池被占满

c、业务响应时间长

d、……

3、问题详情

使用trace分析链路耗时情况,整体耗时链路如下图

大事务带来的问题及解决方案

从图中可看到,业务耗时只有245ms,而整体的耗时时间却是1303ms,两者之间相差了1000ms左右,这个时候很奇怪,时间去哪里了?

二、分析工具

arthas

三、问题分析思路与过程

a、trace调用分析

trace com.xx.horder.service.impl.xxProductServiceImpl byconuponOrder
大事务带来的问题及解决方案

b、从链路耗时结果来看,代理类方法耗时1303ms

[1303.276852ms] com.xx.horder.service.impl.xxProductServiceImpl$$EnhancerBySpringCGLIB$$8c9b855:byconuponOrder

c、业务整体整体耗时才只有245ms,存在一个较高的Http接口调用耗时209ms

[245.071658ms] com.xx.horder.service.impl.xxProductServiceImpl:byconuponOrder
# Http耗时接口
[209.158268ms] com.xx.horder.util.AirHttpClientUtil:postJson() #2005

d、trace调用链中,有个代理类耗时比较高,猜想应该是Spring事务注解(Spring事务是由AOP实现的)

e、使用jad反编译下代码,从图中可看到使用了Spring注解

jad com.xx.horder.service.impl.xxProductServiceImpl:byconuponOrder

jad反编译结果,查看具体代码逻辑(太长了,简化部分无用代码)

     @Override
     @Transactional
public CouponProductBuyResponseDto buyCouponOrder(List<CouponOrderEx> couponOrders, Order order, CouponProductBuyRequestDto requestDto) {
         String orderProductUrl = this.config.getProductSystemUrl() + "/xxController/xxRecord";
/*2005*/     orderResponse = (CreateOrderResponse)AirHttpClientUtil.postJson((String)orderProductUrl, (Object)createOrderRequest, CreateOrderResponse.class);
/*2026*/     this.orderMapper.insert(order);
/*2029*/     if (AirStringUtil.isNotBlank((CharSequence)order.getChannelCustomerNo()) && Integer.parseInt(order.getChannelCustomerNo()) > 0) {
/*2033*/       this.orderIndexMapper.insert(orderIndex);
              }
/*2041*/     for (int i = 0; i < couponOrders.size(); ++i) {
/*2047*/       this.couponOrderMapper.insert((CouponOrder)l);
/*2060*/       this.couponOrderParamMapper.insert(couponOrderParam);
/*2067*/       this.orderSubOrderMapper.insert(orderSubOrder);
         }
/*2069*/     resultDTO.success();
/*2070*/     resultDTO.setCouponOrders(couponOrderExtList);
/*2071*/     return resultDTO;
       }
catch (Exception e) {
       }
}

f、从代码分析逻辑来看大概分几步

i、通过Http接口调用外部系统

ii、执行了5次Insert操作,其中有三次insert在循环里面

g、到这里能分析到两个可能得原因

i、长事务问题,应该是并发较高,数据库操作频繁,导致整体事务提交比较慢

ii、for循环插入问题,批量操作耗时长

针对长事务问题分析,去查看当前Mysql数据库的执行的事务情况及耗时

查看下mysql事务耗时情况

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>50

针对for循环插入问题分析

这个问题应该不存在,如果是for循环操作比较多,Trace的耗时结果会打印出来,但这里只操作了一次,也是可以优化点(针对业务的情况分析)

h、从以上结果分析,证明了我们的猜想

此次问题由于长事务问题导致

具体的解决方案

a、少用声明式事务,通过@Transactional注解

原因:

Transactional注解一般是加载业务Service方法上,会导致整个方法都处于事务中,常常代码中包含远程调用,操作的数据比较多,逻辑比较复杂,导致整体事务粒度比较大

解决方案:

1、使用编程式事务,灵活控制粒度

2、缩小Transactional注解的范围

b、将查询操作,放到事务外,一般查询操作不需要开启事务

c、远程调用之类的,放到事务外

d、事务中避免处理太多数据,数据操作不易太复杂

e、……

本文来自投稿,不代表TakinTalks稳定性技术交流平台立场,如若转载,请联系原作者。

(0)
上一篇 2022年6月20日 下午9:47
下一篇 2022年7月6日 下午1:58

相关推荐

发表评论

邮箱地址不会被公开。