1.背景
所在的活动组经常需要导入券码,然后发放券码。这次导入了100w券码,但是非常耗时,需要1个小时。如果有上千万的券,那么时间非常久,需要达到10多个小时,基于这个点排查原因,提升导入速度。
2.发现
2.1.线上反馈导入100w需要1小时
2.2.查看导入部分的关键代码
上面的代码的背景是每一行导入数据就会调用一次,然后通过
isOnePage
判断,如果达到一页了就会批量保存到数据库里面。
2.3.查看生产日志
发现生产的云日志【导入完成一批次】的日志打印间隔都需要2s,我这里一页的大小是1000条,所以就得定位为什么1000条需要2s这么久,那么第一反应就是数据库插入慢了
2.4.sql插入性能
通过arthas分别trace了couponCodeManager.saveListWithIgnore
和this.updateById(finalEntity)
以及stockManager.updateBySkuId(product.getSkuId(), (long) successNum, true, "券码导入")
,发现这些数据库的操作都很快,也就几十ms,所以数据库的插入没啥问题。
2.5.查看线上调用情况
通过arthas的trace,这里看到了一些奇怪的地方,这个代码每次走这个代理需要2ms多,第一次看到2ms觉得没啥,时间也不长,但是仔细一想,这个函数是一条数据就会调用一次,那么1000条就是2ms * 2000=2s,所以问题就找到了。也就是这个每次循环跑的代理太耗时,到这里大致就能猜到原因了,这个方法代理的有@Transactional
。
2.6.原因
在方法上的@Transactional
导致每次的循环就会和数据库交互,开事务,提交事务。
1000次的循环就执行1000次的数据库交互。性能自然慢了。
解决
针对上面的问题,解决也很简单,就是只有到一页了以后再去和数据库交互,处理数据。不要直接在方法上打@Transactional
注解
3.结果
从之前100w导入1小时提升到了3分钟。
对于@Transactional
的使用以后还是要更加谨慎一些,需要改变以后那种只需要事务就往方法打这个注解的惯性思维。
很多时候一个方法上面可能有rpc等各种和当前事务无关的耗时操作,这样还把事务注解打上面,非常影响性能。