Freewind @ Thoughtworks scala java javascript dart 工具 编程实践 月结 math python english [comments admin] [feed]

(2011-09-13) Hibernate,谈性能

广告: 云梯:翻墙vpn (省10元) 土行孙:科研用户翻墙http proxy (有优惠)

我对Hibernate一直是相当抵触的,主要原因有三:设计复杂,调试困难,性能优化麻烦。前两点之前已经说过多次,这次再谈谈第三点。

之所以突然想到写这个,是因为这几天使用hibernate写了一个小网站。数据表就几个,功能也相当的简单,但是刚刚做完后,我使用ab对首页进行了一下简单的性能测试,结果如下:

   E:\>ab -c 30 -n 1000 [http://localhost:9000/](http://localhost:9000/)
   Percentage of the requests served within a certain time (ms)
   50% 2219
   66% 2281
   75% 2328
   80% 2375
   90% 2453
   95% 2547
   98% 2672
   99% 2734
   100% 3016 (longest request)

可以看到,在30个并发请求下,差不多每个页面都需要2到3秒才能处理完,这个速度相当不理想。我第一时间想到了hibernate的N+1问题,因为我的代码中,没有对这方面进行过专门的处理,直接使用了最简单的方式来实现功能。


于是我将play的application.conf中的debugSql设为true,以便在控制台上看到运行的sql。再次刷新首页,控制台上刷的流下一大堆SQL日志,数了一下,102条SQL。没错,102条。简单分析了一下,原来是在首页需要统计某些分类下的问题数量,我直接用了一个count方法去数据库取的,所以有多少分类,就至少有多少条count语句。现在数据库中的分类还比较少,可以想像再多一些以后,一个首页调用上千条SQL都是可能的。所以首页如此之慢,是必须的。

当然,这可以说是我的设计问题。比如对于这种需要统计数量的,通常的做法,应该是在每个分类中增加一个count字段,当其下的问题增删之后,对它进行更新。查询时,直接使用它的值即可。另外,关于N+1问题,我们也可以通过预先fetch来避免。

我想这些都是可行的,只要你对hibernate掌握得够熟练,你不光可以使用这些优化手段,还可以再利用上它的缓存系统,再次优化性能。另外,对于不经常变动的页面,还可以使用页面缓存,那样的话,就更快了。但是我觉得麻烦之处,不是在这里。而是在于思考重心的矛盾。

Hibernate的目标,本来是为了简化持久层的操作,让我们面对pojo,不需要再关心它下面的数据库(卖点之一是可以随意更换数据库,只需要修改一下配置文件及少数使用了与特定数据库的SQL语句即可)。所以我们在使用Hibernate时,会把思考的重心放在pojo上,以面向对象的方式来设计结构良好的pojo们,而数据库在hibernate的刻意掩饰下,似乎变得无足轻重(比如你甚至可以不关心到底生成了哪些字段)。的确,Hibernate初用起来,感觉很好。对于简单的增删改查,非常方便,基本上调用几个方法即可。对于复杂一些的查询,使用它提供的HQL查询语句,也可以写出来。在不关心性能的情况下,一切都很美好。

但是现实是残酷的,除非这网站只给自己用。一旦数据量多一些,访问人数多一些,性能瓶颈马上就可能出现。对于性能问题,通常我们都会去分析使用的SQL,比如是否有N+1,是否可以把几个SQL取得的数据一次取出,数据库的结构是否需要调整等等。此时才发现,TMD,之前hibernate口口声声对我说:“有我在,你不用考虑数据库了”,到最后,还是得去分析数据库,然后再根据问题,去调整pojo的结构、查询语句等。之前的"以pojo为重心”,到最后还是要回到"以数据库为重心”,甚至双重心。于是,最开始简单优雅的代码,被改得复杂;某些地方,需要使用SQL代替之前写的HQL;最要命的是,hibernate的SQL是动态生成的,必须在日志中才能看到最终的真实SQL,调试起来简直头大无比。

这个时候会觉得,之前看起来那么优雅的代码,现在成了累赘,一方面想保持它,一方面又难免要破坏它。矛盾吧,是不是有种被欺骗的感觉?原以为可以从数据库中解脱出来,最后发现仅仅是为了少写几句代码,得多学习一整个框架,代码还弄得不沦不类。优化性能的时候,不但要考虑数据库,还要去考虑hibernate,更累了。

如果在一开始,我们就以数据库为重心,则写每一条SQL时,都会自然而然地想到性能问题,这样就可以避免大部分的性能问题。而且,因为每一条SQL是都固定的,都是自己手写的,比较放心,还可以利用数据库的工具,去分析它是否存在性能上的问题。不像hibernate,一个大圈回到了原点,问题还更加复杂了。

comments powered by Disqus