[转帖]hibernate 性能调优的几种形式

[转帖]hibernate 性能调优的几种方式

今天在写Hibernate应用程序的时候发现一个程序需要27秒才能加载上来,并且CPU占用情况比较大,MAX=27890kb,MiX=13469kb
   起初想用一个同步的JProgressBar东西来实现和用户交互并且数据库加载数据完全用线程池来实现,觉得用多线程来实现应该比较快吧,可惜我错了, 本来Swing就不是线程安全的,果然不出我所料,出来N个大大的Exception并且全部是不可处理的异常,所以我不得不重新启用一种方案来实现性能 调优。争取在10秒钟内加载数据库数据完毕。
   总结了几点,在MsSql中的排序与MySql中的排序性能上是不同的,虽然top和limit各有千秋,但是我觉得我还是喜欢top多一些,归根到底还 是线程的问题,在hibernate中的性能调优主要涉及到这么几个方面,连接池调优,batch调优,再就是搜索的时候在缓存机制上调优,还有最重要 的,也是最容易忽视的--Hql的灵活运用和多参数构造方法。这个在实际中很常见,其实很多的人喜欢在设计的时候完全用自己定义的持久层对象类直接来操 作,并且取得某一个数据的时候如果不注意细节和相应关联的读取处理数据,会使得整个数据库服务器IO操作异常的高。
   1.仅仅读一个或者少量数据的时候最好关闭lazy,这样不仅使得加载的速度加快,并且能够使用户的体验更加快捷。
   2.在读的关联数据比较多,并且仅仅需要关联别的类中的某一个或者几个数据的时候,我认为最好的方式是 使用Hql和自定义读取类,或者使用多参数构造函数来实现,在性能上是个不错的选择。
   3.在处理高并发和隔离级别比较低的数据库的时候,一般采用的是拒绝长时间的访问,设置多点的检查的可调控性,这个通过服务器端来实现。
4.细节调优
(一)hibernate.query.substitutions true 1, false 0, yes 'Y', no 'N'

这个配置意思是当你在 Hibernate里面输入true的时候,Hibernate会转化为0插入数据库,当你在Hibernate里面输入false的时候, Hibernate会转化为1插入数据库,后面的Y,N同理。对于某些数据库,例如Oracle来说,没有boolean数据类型,就是采用1代表 true,0代表false,因此使用这个配置在Hibernate里面直接用true/false会非常直观。
(二)连接池的选取
A:在hibernate中的连接池大致有两种,App Server和DBCP连接池,一般自带默认的为DBCP,但是在项目中最好使用AppServer,次选Hibernate自带的DBCP连接池。自带的连接池应该做为末选。下面是调优方式:
     hibernate.connection.pool_size 1

     hibernate.statement_cache.size 25

这是Hibernate自带的连接池的配置参数,在默认情况下将被Hibernate采用。

B:如果你采用DBCP连接池,除了要配置DBCP连接池以外,还需要取消掉下行的注释:

代码: hibernate.connection.provider_class net.sf.hibernate.connection.DBCPConnectionProvider

C:如果采用App Server的连接池,假设App Server连接池的DataSource的JNDI名称为"mypool"的话,配置应

该如下:

代码: hibernate.dialect net.sf.hibernate.dialect.MySQLDialect

         hibernate.connection.datasource mypool

         hibernate.connection.provider_class net.sf.hibernate.connection.DatasourceConnectionProvider

其它参数就不必写了,因为已经在App Server配置连接池的时候指定好了。

D:EJB或者JTA中的hibernate 调优
如果你不是在App Server环境中使用Hibernate,例如远程客户端程序,但是你又想用App Server的数据

库连接池,那么你还需要配置JNDI的参数,例如Hibernate连接远程Weblogic上的数据库连接池:

代码: hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.datasource mypool

          hibernate.connection.provider_class net.sf.hibernate.connection.DatasourceConnectionProvider

          hibernate.jndi.class weblogic.jndi.WLInitialContextFactory

          hibernate.jndi.url t3://servername:7001/

注意:在EJB或者JTA中使用Hibernate,需要取消下行的注释:

代码: hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory

E:在高并发和CRUD频率高的数据库中进行性能调优 主要针对Oracle和MySql集群
     hibernate.jdbc.fetch_size 50

     hibernate.jdbc.batch_size 25

这两个选项异常重要-将严重影响Hibernate的CRUD性能!



具体理解和解释:Fetch Size 是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数。例如一次查询1万
               条记录,对于Oracle的JDBC驱动来说,是不会1次性把1万条取出来的,而只会取出Fetch Size条数,当纪

               录集遍历完了这些记录以后,再去数据库取Fetch Size条数据。因此大大节省了无谓的内存消耗。当然

                                   Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch Size越小,读数据库的次数越多,速度越

               慢。这有点像平时我们写程序写硬盘文件一样,设立一个Buffer,每次写入Buffer,等Buffer满了以后,

               一次写入硬盘,道理相同。
关于Batch我就不详细解释了,相信用过addBactch()的人都知道.


Oracle数据库的JDBC驱动默认的Fetch Size=10,是一个非常保守的设定,根据我的测试,当Fetch

Size=50的时候,性能会提升1倍之多,当Fetch Size=100,性能还能继续提升20%,Fetch Size继续增大

,性能提升的就不显著了。因此我建议使用Oracle的一定要将Fetch Size设到50。 (主要针对大型数据和多格式的数据)

值得注意的是:并不是所有的数据库都支持Fetch Size特性,例如MySQL就不支持。MySQL就在上述查询中总是一下就把1万条记录完全取出来,内存消耗会非常非常惊人,不过谁叫T是开源呢。

如果你详细的写过JDBC批量处理,在执行插入的时候,你会发现大容量的数据的插入非常的消耗时间,经过我的一个测试:插入100000条数据到 mySql中,Hibernate速度至少是JDBC的两倍,我最后一研究,原来是因为Hibernate使用了Batch Insert,而我自己写的JDBC没有使用Batch的缘故。以我的经验来看,Oracle数据库 Batch Size = 30 的时候比较合适,50也不错,性能会继续提升,50以上,性能提升的非常微弱,反而消耗内存更加多,就没有必要了。
注意:值得注意的是在Jdbc 使用Batch的时候是不一定能够产生很大的性能改变的,看机器和数据库而定,不敢轻易下结论。


F:可滚动结果集和CGLIB进行调优
首先说明下什么是可滚动结果集:一般在statement执行代码后会返回结果,一般的采用是迭代从前至后,这样对有的时候是不需要的,例如我想要从搜索 出来的1000条数据中拿出从第500条开始的后300条,那么按照next()迭代的话会浪费前500次迭代的时间,所以可滚动就很需要的。

代码: #hibernate.jdbc.use_scrollable_resultset true

设定是否可以使用JDBC2.0规范的可滚动结果集,这对Hibernate的分页显示有一定的作用,默认就好了。

代码: #hibernate.cglib.use_reflection_optimizer false

进过测试发现,在Spring中的CGLIB也是可以进行性能调优的,hibernate中的cglib和反射编辑是默认是打开的 ,启用cglib反射优化。cglib是用来在Hibernate中动态生成PO字节码的,打开优化可以加快字节码构造的速度,从而提高数据库速度。

G:其他杂项配置

代码: hibernate.show_sql false

是否将Hibernate发送给数据库的sql显示出来,这是一个非常非常有用处的功能。在调试Hibernate

的时候,让Hibernate打印sql语句,可以迅速找到问题。

代码: #hibernate.connection.isolation 4

指定数据库的隔离级别,往往不同的数据库有自己定义的隔离级别,未必是Hibernate的设置所能更改的

,所以也不必去管它了。 关于隔离级别的问题我就简单介绍介绍:有 未提交读,可提交读,可重复读,可串行化四种,级别依次升高。


综上所述:我认为在数据库的性能调优是需要视具体情况来定的,在设计中时间和空间是平衡的,就连数据库中都存在空间和时间互换来提升效率的问题,所以我们有时候不能因为嫌多写几百行代码而不去写,该是性能调优的还是要调优,不能偷懒,严谨对待。

最后我的程序调试完了大大超过了我的预期:开启工厂6.3second,打开session 47ms,执行第一次从27秒调优到4s,第二次再次读取1.2秒,淡淡中我看到了前进的曙光。