Hibernate的性能(转贴)
xiecc:
我们的项目从去年12月份启动,采用了Struts+Hibernate的架构,一开始使用Hibernate的时候速度极快,对象操作异常方便,大家都说爽歪歪。
可惜好景不长,随着我们对象关系的不断复杂,数据量的不断增加,Hibernate的性能急剧下降。具体表现为:我们在设计对象时采用了很多的one-to-many和many-to-one的关系,在取某个对象的几个简单的属性时,它会把所有关联的子对象都取出来,经常出在取一个简单属性的时候,调试窗口的SQL语句一屏一屏地往下闪。到最后我的一个test跑完需要12分钟。
在忍无可忍之下,我们开始性能优化方案,以下我们优化所做的一些事情:
1、将所以one-to-many的关系里将lazy设成true
2、修改hibernate.properties,增加了以下两句:
hibernate.jdbc.fetch_size=50
hibernate.jdbc.batch_size=100
3、调整WebLogic的pool
4、利用Hibernate提供的CGLIB Proxy机制,使many-to-one关系的子对象也可以lazy initialization
(但是我发现调试窗口里仍会有取子对象的SQL语句,但速度确实快了)。
5、利用Hibernate提供的Cache机制,对关键对象使用Cache
结果优化以后,我的test可以从原来的12分钟变成50秒钟跑完。
原以为万事大吉了,但当我们面对客户的时候,才发现我们系统的性能还远远不够。
我们现在系统试运行约两个月,经常在数据保存或者查询时等上一分钟甚至两分钟。
由于客户原来的系统用asp+SQL Server写的,速度很快。二者一对比,我们就被客户骂得惨不忍睹。
优化真是一件很烦人的事,在不改动系统框架的情况下,不知还有哪些提高系统性能的方法?
freecode:
同感,虽然我不用,不懂hibernate.
前段时间,我们做了个项目,对一些取数的过程,采用了javascript脚本,再通过bsf编译,运行时,时间巨长,人家说以前用foxpro做的,快多了,弄得我们很没面子。
dhj1:
我也用 Struts+Hibernate 做大型项目,在并发很高时,每天4500人次访问量的情况下,性能也相当不错.
做的时间有几点考虑:
1.大东东.如果很多很多的one-to-many和many-to-one的关系. 必定会影响性能,我刚学习Hibernate 时就有这种直觉,所以我们没有用one-to-many和many-to-one的关系.而是象SQL一样的去操作表的关系标识符.
2.如果超大的系统,最终必须生成HTML的文件.就是有数据库中有数据更新时,自动生成一个HTML文件.大多数用户是在只读状态.在只读状态下就只去显示HTML文件,节省很多资源.
3.更用CHACHE表技术,把访问量高的记录自动提到CACHE表中.
xiecc:
谢谢dhj1给我提的建议。
在hibernate网站上看到的好多资源几乎都说hibernate的性能不错,而且很多人开发的系统性能也不错,包括dhj1的,看来hibernate无罪,是我们设计得太滥了。
不过还是有点疑问。
1、dhj1提到的第一点很有道理。我们确实在有些关键的地方用了标识符来关联。但是我们这个系统的关联实现太多了,如果所以有东西都用标识符作关联的话,那我们的实体层设计就退化成为面向关系的ER图设计,那我们要Hibernate何用?我用感觉用Hibernate时最大的便利不是在写代码的时候用对象的操作代替SQL语句,而是在建模的时候可以用面向对象的思维把很复杂的逻辑用UML图表示出来,然后直接转化成实体。所以我们在性能影响太大的地方采用了面向对象和关系相结合的方式,但在更多的地方仍然只能采用对象关联的方式。
2、生成静态HTML,对特定的系统确实有用,但我们系统中的数据几乎都是动态的,所以实现起来有困难。而且我也不太清楚这样做的难度有多高,具体怎么实现,请dhj1指点迷津。
3、Cache我们已经采用了。但proxy的用法我至今仍然有点迷糊。
在Hibernate的文档里似乎只要在定义文件里加这么一句就可以了:
<class name="eg.Order" proxy="eg.Order">
但实际使用时我们发现这样做之后,Hibernate取数据时的SQL语句似乎一句都没有少。
我们现在的想法是自已来实现Proxy类,它的接口与实体完全一样,在Proxy里SQL语句来实现取数据操作。不知是否可行?
sanwa:
我在设计系统时,对每个对象图都会采用两套映射模型,
一套用于映射实际的UML,当然包括引用,这套对象图主要用于根据关联对象的属性来查询对象时使用,很少用于更新数据,除非是聚集类.
另一套映射,把对象引用映射为Long型的属性,传递到业务层或表示层,需要相关的对象引用时,再用这个引用来load对象.而且,对于需要load的对象可以使用代理或直接多映射一个简单类来处理.
另外,对于大数据量的查询或表的关联层次较深(超过三层),建议采用jdbc直接处理.
方世玉:
我们现在也正在用hibernate进行项目开发
我认为不是所有相关的表都要做one Many,many one映射的
比如说一个用户和他的定购业务,我们做了双向关联。
但是这个用户的话单就不能和用户做任何关联了。话单表每天都有数十万条,就是做manyone关联也非常吓人,宁可到时候用hql来查询
xiecc:
呵呵,请教sanwa,这两套映射模型如何在一个应用中同时使用?
dhj1:
我用hibernate,是可以减少开发工作量. 特别在开发中或维护过程中对表结构的改动,用HIBERNATE是很方便的.
做了DAO后,对表的父子关系等的处理,通过ID标识,也只是两三句程序语句.操作也很方便,而且更灵活.
生成静态HTML,以后我做过这样的系统,并在XXX省信息港网站上大量使用,性能当然不错,同时1400人在线(真正的在线,不是那种用网页搞个几分钟刷新一次延时的那种在线),性能也不错,开发当然会有一些难度.
我说的CACHE不是说用HIBERNATE的CACHE.而是自已开发的,把访问量高的信息自动放到一个表中去,这个表保证只有100访问量最高的条记录.多于100条记录的就出去了.
sanwa:
举个例子,比如用户的权限,在我们的系统中涉及用户(User)、权限(Acl)、组件(Component)、组件类(componentDomain)等几个对象的关联。
第一套映射图,反映他们的实际关系,即对应UML模型。User和Acl的关联映射为idbag,不要直接映射为set,因为在我设计的关联表中存在代理主键,代理主键在第二套映射图中实际为用户权限的id.
第二套映射图,只映射用户(UserSO)和用户拥有的权限(UserAclSO),是one to many的关系。
后缀SO表示相关类的简单对象映射类。
这样,当客户端需要获取某个用户的所有权限时,直接用第二套映射图。返回的集合中就只包括Acl的id。如果要获取用户对某个组件域的权限,则用第一套映射图,用强大的HQL查询,再转换为第二套映射图返回到客户端。
当更新用户的权限时,也用第二套映射图,直接操作UserSO和UserAclSO,传回更新。
使用类似的设计策略时,对many to many的关联表,都采用代理主键,而不是联合主键,这样,两套映射图都较容易存取数据。只是,多数情况下用第二套映射图。
当然,我的程序架构是Swing + Session Bean + DAO + Hibernate + DB,Swing和Session Bean的通信可以用HTTP或RMI,在我的架构中,lazy loading发挥不了多大的作用,才采取这种策略。如果在lazy loading可以发挥作用的地方,对大多数对象图,是没有必要采取这种策略的。
另外,在我的架构中,swing层有一个组件是可以根据一个ID来加载这个类的属性,直接用jdbc实现的,独立于Hibernate
xiaoyu:
我也说上一句吧。
虽然HB是好,方便,但有关数据库设计的一些性能原则还是要考虑的。
毕竟它只是Mapping而已。
所以也要设置索引等东西。
jxb8901:
上面的proxy和cache没有任何关系,只是用于lazy loading,请看hibernate中文文档第5章:
java代码:
proxy (可选): 指定一个接口,在延迟装载时作为代理使用。你可以在这里使用该类自己的名字。
coolwyc:
为了在性能上得到平行,对many-to-one不直接使用关连,例如:user&ACL,取user时,不取ACL,要取ACL就先取user,再取ACL,毕竟应用取user的频率比取ACL高,没必要在取user是硬要把ACL一起取出来.
还有其他many-to-one都和这个很类似,如果many很少的话就没问题,例如:人&宠物,通常一个人只有少于等于一个宠物,这种情况取人的时候把人跟宠物一起取出对系统影响不大
willmac:
hibernate本身的性能非常好,关键在设计本身
和你的数据库本身,以及你的访问量和数据库的性质
针对不同的环境一定要有不同的设计的,一套设计肯定不能适用于全部的环境
我举个典型例子来说吧
你的hibernate设计可以采用单session的方式,也可以采用多session的方式,应用环境不同,结果也大大的不同。当用户人群少,数据库记录两低的时候,多session的设计是非常有优势的,这就是为什么那么多人
反对使用threadlocal管理session的主要理由把,的却
把两种设计都放在这里,你会发现多session的性效要比
threadlocal的强太多了,并且编程也异常的容易,这种例子,我不再举了,你在这个论坛上都可以下的到。可是
当人群变多,数据库记录海量增长的时候,我们发现问题来了,我们做的应用无限制的吃去内存,应用的响应开始变慢,这是为什么呢?不知道大家是否理解究竟什么是session,其实从根本上说就是一张hashmap,这样大家就好理解了,当不同的用户,同时访问应用的时候,不同的人建立不同的连表,然后释放,而当并发人群急速增长的时候,于是问题就来了。那怎么办,threadlocal没有其他的办法,你要解决一个同步的问题,我们只有一个session,你不能够同时往里读取数据,然后又写入数据的,并发用户这么多,你只可以让他们一个一个得来!!!
哇,这样效率不是太低了,的确,我一开始就说了,小型系统,使用threadlocal绝对是低效的做法的,可是当规模上来了之后,我们再来看,内存不再无限制的开销,cpu德负荷也降下来了,唯一一点发生变化的是,用户的客户端可能多等了0.001秒钟,一个在服务段队列的时间,这就是设计。
讲了一个设计的例子,我们来看多对一,一对多,会让我们应用的效率降低么?
请注意hibernate只是帮助我们完成了mapping 的工作
原来的一对多也好或者多对一也好,本来就存在的,之所以让你觉得效率低,是因为在得到父亲后,还要去把每一个儿子给读出来,可是注意,你只是多执行了一条sql而已,为什么会慢的呢,我想问题还是在设计之上