最近项目新打的版本,过不了多长时间,项目就会挂掉。情况就是处于一种假死的状态。索引查询都很慢,几乎进行不了任何操做,慢慢卡死。
而后咱们再发版时,只能基于以前打好的war包,替换或者增长class文件。mysql
因为以前代码作过一次大整顿,提交的代码比较多,因此经过回滚版本的方式解决,比较困难。一是由于整顿的成果不能白白抹杀;二是那么多文件,靠人工挨个对比查找,比较困难。sql
以前, 一直对目前项目的打包方式心存质疑,因此此次发生问题时,我首先怀疑的对象是Jenkins和生产的Tomcat服务器。我经过堡垒机链接到生产时,发现经过Jenkins启动应用程序,会启动两个两个tomcat进程。数据库
而后,这彷佛更加坚决了个人见解,立刻就找到了运维,可是经确认后,是没有问题的。一个是用root用户启动的,一个是用tomcat用户启动的。一个守护进程,一个应用进程。tomcat
排除了服务器的问题,开始正面考虑程序的问题。
从新发项目有问题的版本,Dump下来的日志,而后迅速回滚观察。单台机器的dump日志有5个G:服务器
经过Memory Analyzer分析,在Leak Supects Report 视图中,有以下分析结果:运维
上图所示,共有三类问题a、b、c;还有一些其余的,类型为d。dom
先来看第一个问题(后来发现,前几个问题都是同一个问题)spa
先点开Details看一下:线程
上图显示了一个很明显的有问题的线程:地址是0x7c8ff3df0 ,名称为pool-16-thread-1。
经过《Accumulated Objects in Dominator Tree》视图能够看出,在该线程中,存在一个大的List对象,List对象内存放了大量的mysql的jdbc对象。3d
咱们想看看JDBC对象里面堆放了哪些数据。接下来咱们打开《open dominator tree for entire heap》这个视图:
找到名为0x7c8ff3df0 pool-16-thread-1的线程。如图也能发现,这个线程占用了大量的空间未释放。一层层打开里面的存放的对象:
这里的数据,是咱们的一张用户表的数据。因此这就能够得出结论:一个线程内,一个list内存放了大量从数据库中获取的用户对象!
想到这里,咱们又去看了b、c的问题描述,也是一样的问题。估计是在不一样时间点,经过gc已经回收了部分。
而后,我刚才看了a问题的details信息,接下来咱们看下a的stacktrace 堆栈信息。
如上图,问题就很明显了,在Service的112行中,调用的findByCustomerID方法中,有扫描全表的操做。通过分析,找到对应的位置,对应的代码为:
customerID = StringUtils.isNotBlank(customerID)?customer.getCustomer().getCustomerID():null; Customer oldCutsomer = customerService.findByCustomerID(customerID);
显而易见,流程走到这里时,customerID永远为空,那么customerService.findByCustomerID(customerID)方法,会执行扫描全表操做。因为该表数据量巨大,开发所认为的用户每次执行的索引查询,实际上都成了慢查询,并且须要返回全表数据。大量线程过来,占用大量数据库链接,致使数据库链接数不够;而每一个线程处理时,须要大量时间, 致使项目处于一种假死的状态。
一、在工做中必定要规范代码管理,才能提高开发效率,下降企业遭受损失的风险;
二、经过此次实践发现,目前开发权限小,致使跨部门协同效率不是很高,接下来的开发中,立项之初就创建项目开发流程,从而提高开发效率;
三、解决问题的方法遇到瓶颈,尝试第二种方法,多角度多层次对问题进行突破。
做者:刘正权