数据库查询速度极慢【个人工作问题解决过程记录】

  最近客户那边报了一个问题上来,问题:门店反馈JPOS界面查询“分类销售报表”,查询一个月的数据一直卡在查询界面,查询一天的数据,至少要3分钟,据了解门店之前是没有问题的。

  我拿到客户现场的数据以及日志,把现场数据安装到我的开发环境中,查看下日志,日志没有任何报错,收银员的操作也没有任何不规范之处,然后就果断不管日志了,重现了问题,发现没有报错,进度条一直显示“正在加载数据”,所以想到用visualVm中的线程监测工具对他进行监测,监测结果显示,线程没有死锁,一直处在Runnable状态,入下图:

 线程监测没得到什么头绪,然后想看看堆的情况,然后打开visualVM对内存进行监控,监控了一段时间终于有了一点头绪了,入下图:

从visualVM对垃圾收集器的监控来看,新生代Eden区,老年代Old区都呈现锯齿状,显然这个有问题,不正常。Eden区不断的出现新生对象,等到下一次MinorGC的时候Eden区和Survivor区的存活的对象会通过空间担保机制直接进入老年代,老年代内存达到默认FullGC的百分比的时候就会触发一次,监控还显示了Eden区会有非常巨大的对象产生(是Eden区直接从13M达到80M)。所以光从这个统计也不能知道具体怎么回事,所以决定对该进程的垃圾回收信息以及堆进行一个分析。

  用-XX:+PrintGCDetails打印出垃圾收集信息,并且使用JConsole.exe中的MBean创建一个堆转储文件(用Eclipse Memory Analysis进行分析)。垃圾收集的日志分析得到,开始的时候Eden区的内存大小只有4M,之后慢慢的扩大,这个扩大Eden区的操作显然是需要时间的,老年代也是如此,所以不如直接把Eden区和老年代的大小固定为他们各自的最大的值。之后垃圾收集就很规律,Eden区不断的产生新的对象在老年代中的FullGC中被回收点,虽然虚拟机在不断的做创建和销毁的操作,但是从来没有溢出。所以垃圾收集日志对我的帮助也不大。

  下面看堆转储文件的分析,使用JConsole.exe产生一个堆的快照,H:\jpos.hprof,然后在Eclipse中用插件Eclipse Memory Analysis打开进行分析,效果如下:

  

  上面的图片说明了ConCurrentCache以及SessionFactoryImpl在堆里面占的比例是最大的,所以在Eclipse Memory Analysis分析器中显示着两种对象很可疑,那为什么会产生这2种对象呢?暂时不解释。看到这里,我第一反应是Hibernate的缓存导致的,但是想了下,我的hql只有一条,用来统计数据的,也不需要不断的生成对象,不断的销毁对象,当然想到了这一点,我离问题的真正原因也更近了(实际是更远了,我跑偏了方向,只是此路不同,所以我离真相更近了),我萎了验证到底是不是Hibernate缓存导致的,我决定把Hql转换成Sql,直接调用JDBC底层的方法进行统计数据的查询,结果出人意料,跟我想的完全不一样,就算我换做JDBC查询,速度也一样的慢,VisualVM中的VisualGC插件显示垃圾收集器一样的锯齿状出现,到这里,有经验的大牛应该知道了,问题在哪里了吧?数据库三张表,每张表2w条数据,也不算大数据。

  最后我想这应该数据库字段没有加索引导致的,给每个表出现的字段加上索引,问题马上解决。

  现在我们再看上面为什么会不断的生成对象,再销毁对象。原因是JPOS收银机用的是内嵌式Derby数据库,当执行这个操作的时候,内存中会不断的加载数据匹配,匹配完一批再匹配另一批,直到把所有的数据都匹配完为止,所以VisualGC中会显示不断的在Eden区创建对象,然后不断的通过担保机制到老年代,然后做FullGC,这也就是为什么ConCurrentCache以及SessionFactoryImpl在堆里面占的比例会那么大。

  虽然问题的解决绕了很多弯路,但是至少学习到了很多知识,熟悉了各种工具的使用。

 

posted on 2013-08-28 21:28  Kahuna  阅读(18168)  评论(0编辑  收藏  举报

导航