运维 - hibernate大事务导致插入数据变慢事件侦破 【arthas】

现象

同事反映服务器上有个操作执行起来很慢。具体情况如下:

一个业务需要往数据库中写入数据,

第一次循环写入10000条数据,很快,700多ms

第二次,第三次...越往后越慢

第50次的时候,需要10几秒了。

 

初步定位

查看应用服务器和数据库服务器的负载情况。

数据库服务器,cpu、内存、磁盘IO都很空闲

应用服务器,cpu占用率很高,内存和磁盘正常。

初步判断大几率问题出在应用服务器。

 

侦查

arthas请出。

1、dashboard 查看大体情况

1577787773349

看出线程36有问题

2、thread 36 打印出该线程的堆栈信息

这里一般查找我们自己写的程序即可,sun,spring的不用去管

1577787958855

这两个方法是我们程序的,为了稳妥起见,我选择了doWork方法开始追查(防止sort方法只是路过这种情况)

3、trace app....XXService doWork 追踪该方法的内部执行情况和耗时

注意:如果发现trace 出来的结果不正常 或者 trace 不到东西, 执行 reset 恢复下环境。

1577792204254

结果中有两个比较大,仔细比较total,发现总耗时sort方法远大于 findByxxx那个方法。

另外耗时后面表明了count,表示这个方法在trace的那个方法中被循环执行了多少次。

如是继续 trace sort这个方法...一直下去,直到追查到 org.hibernate.event.internal.AbstractFlushingEventListener的flushEverythingToExecutions方法。

1577805268742

截图中我们可以看出 flush预处理 和 flush 两个动作占用的而时间较多。

4、追查结束,记着reset一下退出arthas。后面就是研究flushEverythingToExecutions中的那两个flush方法的事情了。

后来研究发现 如果我们大量insert后,persistenceContext.reentrantSafeEntityEntries() 中会存放大量未提交的entity。

如果我们此时做查询,hibernate为了避免之前插入的数据能让查询语句查到,会将所有的未提交的entity全部都flush。

做flush预提交和 flush提交里面都会对 未提交的entity 循环一个个进行处理。

未提交的entity越多,循环就越久。

最可怕的是,虽然这次flush了,但这些entity只要没提交,就一直会在persistenceContext.reentrantSafeEntityEntries() 中,每次查询就都会走一次这样的过场,耗费的时间可想而知。

 
解决

找到问题就好办了。

原因就是事务太大了,导致存在太多的未提交entity。

将事务拆小,insert一批后及时提交,问题解决。

 

总结

解决并不是文章重点,该文主要是记录一下如何通过arthas追踪定位问题。

posted on 2019-12-31 23:33  HB1  阅读(927)  评论(0)    收藏  举报

导航