论坛首页 Java版

不做技术的奴隶

浏览 48415 次
该帖已经被评为精华帖
作者 正文
最后更新时间:2007-06-02

 


    近日,上论坛中,看了Ibatis和Hibernate的帖子,看后,心里觉得的憋闷,不说不快, 其实Robbin之前有一个帖子,都说过了,但在这里,我想更细化一下: 

    1. 库表关系的复杂度,首先取决于需求,不取决于设计,设计能力强的人,也要遵守库表设计的规范,从巴克斯三个范式上,原则上也要遵守。不能说用了Hibernate,自己的库表设计能力就强了。不能为了用Hibernate,就去一味批判复杂的关系不对。复杂的关系设计对不对,首先取决于是否有复杂的需求,其次才取决于设计者的能力。

    2. 只要你用的是关系数据库,就必须要明白,为什么叫关系数据库,而不叫面向对象数据库,把面向对象的那些观点,拿到库表设计上,后期维护和调优上,你要担起责任,不能让开发人员替早期决策人员擦屁股。我见过有的人,打着OO和扩展性的旗号,硬生生的把一个表,拆成了三个表,而这三个表,本来,只需要增加一个类型字段,再做一些冗余,就可以是一个表。现在查询时,还要把这三个表Union到一块来查。当需求变更时,增加一个字段,不仅要改变三个类,还要改变三个表,简直是乱伦。

    3.One-One的库表设计,对于DBA来讲,并不是一个best practice的设计。不能为了Hibernate,刻意把大表拆成小表,再用几个小类,做成One-One的映射关系。整体性,是不能随便的分割,毕竟开发人在调试、测试和维护的时候,更喜欢看数据库里的数据,本来一个SQL,就查出来,现在要到多个表中去查。

    4. 增删改存的实体维护
       Ibatis比不上Hibernate,说实在话,现在让我写SQL来维护一个多对多关系的实体维护,我都要考虑上半天,别说写代码了。

    5. 你需要写原生SQL吗
       首先你要确认,你项目的要求不需要写原生SQL,再来讲Ibatis和Hibernate的好坏,在写原生SQL上,特别是动态生成的SQL,ibatis比Hiberante有得一拼,ibatis就像一个模板一样,将SQL写在配置文件当中,集中配置,特别方便技术领导者监控项目成员写的SQL好坏,而且没有什么学习曲线,就写SQL就完事了。

       有人会说,Hibernate也支持写SQL,但是写代码当中,就失去了原来基于Hibernate的DAO的简洁性。那个DAO一点也不简洁,如果你将动态拼SQL的代码也放在DAO当中,那个DAO就会充斥大量的If 。。Else。。之类的语句,一坨一坨的,非常的壮观。

       还有人会说,Hibernate也支持命名查询,将SQL写在映射文件当中,但是命名查询,只支持占位符固定的情况,也就是说,where a = ? and b = ? and c=?,是三个问号,就是三个问号,传参时,少一个都不行。但是很多项目的查询,都是动态的,也就是说用户选了这个查询条件,才会生成这个占位符的。

       Hibernate办不到。


       还有人会说,我自己用Hibernate写一个框架,也可以做到,那你写的绝对可能比Ibatis好,也可能差,你要造轮子,谁来拦不着。
  

    5. 调优
       早期调优,有些Bad SQL,其实在code review阶段,只要看看Ibatis的SQL配置文件,就可以扼杀掉的,如果使用HSQL,可能不会被发现,因为它不仅隐藏在代码当中,有的时候,还需要程序跑起来,通过日志打印出SQL或者通过其它工具如P6Spy来看的出来。

       后期调优,既然是后期调优,我想就一定是遇到了瓶颈,可能要在库表上做冗余,可能要检查那些Bad SQL,可能要修改代码,可能要动用DBA层次上的一些调优手段,那么调优越深入,Ibatis的优势就越能体现出来,比如说增加临时表,中间表,增加冗余字段等。

    6. 开发速度
      
       如果项目当中,没有一个Hibernate高手,你的项目又相对的复杂,不仅有复杂的库表关系,还有大量的报表查询,那么使用Hibernate,速度上逊于Ibatis.

       问题在于,怎么样算是一个Hibernate高手,别看论坛上,那么多人,群情激奋的在说Hibernate的好,有谁真的是高手?

     7. 平台移植性
       如果你的项目要做产品,而且打算基于多个数据库平台的发布,使用Hiberante是没有说了。

    8. 维护性
       如果不考虑移植性,Ibatis的可维护不差于Hibernate,库表变动引起实体类变动时,HSQL也会有改动,有人说不用改,说这话的那个人可能整天只会有select * ,如果ibatis也是这样写,也不用动了。

       HSQL好阅读吗,From order,确实很简单,但实际当中,这跟拿HelloWord做例子,有什么区别?

     
   
    9. 我在实际项目当中的运用
    项目背景:
       我自己从Hibernate2开始使用,我现在也不认为我是Hibernate高手,我也没有时间去钻研Hibernate,更深的东西,我也不喜欢坐在开发人员旁边老半天,去帮他们解决Hibernate遇到的问题,因为我自己还有很多事要做。

       我的项目当中,在Hibernate方面,还有一个比我更强的人,他也很烦去看Hibernate打印出来的sql,看上老半天,再调上老半天,项目进度,嗖嗖的过去了。

       水平越高的人,任务越重,很少有时间和耐心去解决一般性的问题。

    最终的运用:

       在基于Spring的容器事务管理之下,
       增、删、改、存及在事务中的查询,使用Hibernate。
       非事务性的查询及报表,都用Ibatis,维护非常的直观方便,开发速度上也快很多。

       我觉得现在技术换代很快,使用一项技术,首先是要快速的解决问题,然后要学习他的思想,那些整天死抱着Hibernate,自认为学习到ORM的设计技巧的人,就去继续的学吧。

       我已经会用Hibernate的一些方面,我觉得够用就行了,犯不上,天天钻研HSQL,如果有时间,我觉得躺在草坪上看看Unix的编程艺术,看看代码大全,看看Oracle的编程艺术,比看Hibernate的SB书要惬意多了。

       简单能够带来快乐,用过EJB,再用Spring的人,都有体会,那简直是一种思想上的重生。


       Ibatis的设计者认为,在新的项目当中,可以使用Hibernate,在旧的遗留项目当中,可以使用Ibatis,不明白,他为什么说这样的话,这与新旧项目有什么区别? 用不用Ibatis,我觉得对我上面说的几点有关。

 

 

  

 

 


   
最后更新时间:2007-05-05
其实ORM(不特指Hibernate)在实际项目当中,设计和运用的比较好的话,在OLTP类型的应用当中,性能是肯定要远远超过SQL Map的(例如iBATIS),其根源在于ORM可以透明的插入对象级别的缓存,而SQL Map是做不到这么细颗粒度的缓存的。而当一个web类型的OLTP应用的数据量和用户量达到一个相当大的规模的时候,应用级别的缓存无疑最有效的性能提升手段,这个时候ORM的威力就体现出来了。

http://www.javaeye.com/topic/72578

关于这一点,在Gavin King上海交流研讨会上面我已经谈了这个话题。当然对于ORM的方面的理解,我自己也是一直到去年底才真正搞明白的。这多少也是因为使用了RoR的AR以后,对ORM的看法更加客观之后才能够得出来的。

当然,不得不说的是,Hibernate虽然是一个强大的ORM,却不是一个非常易用的ORM,鉴于大多数程序员的编程基础都不过硬,要求他们良好的运用Hibernate基本是不可能的。这个时候也许iBATIS会给他们更可靠的编程能力。
   
0 请登录后投票
最后更新时间:2007-05-05
to robbin:
   ORM, 我现只能看到关系映射与维护这一层,至于OLTP这种类型的应用,我也没有做这样的项目,所以看不到这么远。
   不过在实际的应用中,有二级缓存,总比没有强,但作用多大,我实际上是讲不清楚的。因为在实际的调优过程中,这一项,一直是打开的,并且用的是TreeCache。但瓶颈点不在这个地方,所以感觉效果并不是很明显。也许去掉二级缓存,就能感觉到了。
   
0 请登录后投票
最后更新时间:2007-05-05
另外,如果使用Hibernate,大部分的应用,对于延迟加载是必须要打开的,否则就玩不转了。
   但打开延迟加载,一般都使用OpenSessionFilter.无论如何,在连接的保持的时间上,要延长一些。
   在论坛当中有一个精华帖,说道了这个性能问题,对于一般的应用是没有关系的,无非是加上了response.write()的时间,这个时间对于不同的页面,视数据量不同,时间有长有短,不定。
   但要不要在自己的应用当中使用OpenSessionFilter,对我而言觉得也是一个比较难的决策,如果你使用了,但在后期提交压力测试,通不过,再返工,就没有那么容易。因为如果不用这个Filter,代码就不是这样子写的。
   
0 请登录后投票
最后更新时间:2007-05-05
OneEyeWolf 写道
to robbin:
   ORM, 我现只能看到关系映射与维护这一层,至于OLTP这种类型的应用,我也没有做这样的项目,所以看不到这么远。
   不过在实际的应用中,有二级缓存,总比没有强,但作用多大,我实际上是讲不清楚的。因为在实际的调优过程中,这一项,一直是打开的,并且用的是TreeCache。但瓶颈点不在这个地方,所以感觉效果并不是很明显。也许去掉二级缓存,就能感觉到了。

  


OLTP就是相对于OLAP来说的,联机事务处理,普通的不涉及到大数据量报表分析查询的系统其实都是OLTP,特别是一般的互联网网站都是OLTP。

二级缓存在数据量小访问量小的时候,效果看不出来,当然也需要程序员在开发的时候就要有针对性的设计。但是当数据量大的时候,效果就出来了。一般的程序员在软件交付使用以后,不再继续跟踪这个项目了,所以一般不会涉及到这个层面。因此无论是ORM运用不好造成的严重性能问题,还是Hibernate良好运用对于性能的巨大提升,一般都没有一个清楚的认识。他们只知道通过看到log里面一屏一屏滚过的SQL就觉得Hibernate有问题了,呵呵。

Hibernate的二级缓存是针对对象级别的,即使你打开TreeCache,如果你没有针对具体的对象设置cache选项,一样没有用上。另外就像我在演讲里面提到过的,用不用ORM,用不用缓存,这是一个架构问题,不单纯是你打开不打开TreeCache这么简单,而是涉及到你的整个数据库表结构应该怎么设计,对象关联怎么安排,查询语句怎么写的各方面问题了。

这其实是一个很大的话题,不展开谈了。反正我的观点还是那样,如果对ORM的理解不深刻,对整个架构设计掌控能力不到位的话,那么还是用iBATIS吧。
   
0 请登录后投票
最后更新时间:2007-05-05
OneEyeWolf 写道
   另外,如果使用Hibernate,大部分的应用,对于延迟加载是必须要打开的,否则就玩不转了。
   但打开延迟加载,一般都使用OpenSessionFilter.无论如何,在连接的保持的时间上,要延长一些。
   在论坛当中有一个精华帖,说道了这个性能问题,对于一般的应用是没有关系的,无非是加上了response.write()的时间,这个时间对于不同的页面,视数据量不同,时间有长有短,不定。
   但要不要在自己的应用当中使用OpenSessionFilter,对我而言觉得也是一个比较难的决策,如果你使用了,但在后期提交压力测试,通不过,再返工,就没有那么容易。因为如果不用这个Filter,代码就不是这样子写的。
  


这到不是什么问题。OpenSessionInView造成的Session资源不能及时释放的问题,只会出现在互联网应用当中。对于企业内网应用,网络速度足够快,response.write()不会造成多少延时。即使在互联网网站当中,你只要在Tomcat前面放一个apache web server,隔离慢速网络连接和tomcat的response.write速度差异就行了,tomcat在把response body写入到操作系统的TCP buffer之后,就已经释放session了,然后apache会慢慢从buffer里面取数据通过慢速互联网连接flush给浏览器的。
   
0 请登录后投票
最后更新时间:2007-05-05
我做的就是电子商务网站应用,对于性能是有一定要求的,上线前是必须要提交压力测试的,应用服务器用的是WS + apache, 但这个险我是不敢冒的。
    我这种应用也算是OLTP的应用了,我看错了,以为Robbin说的是基于数据仓库和挖掘之类的应用了,不过我现在的网站应用,以及以前所做的管理系统,出的问题,都是出现在开发人员对Hibernate的特性掌握的不好,对于复杂的查询, 难以应对,就又回归到通过Hibernate写原生SQL的道路上。
    所以我觉得,对于一般的应用,问题的瓶颈点,还不到这一级。
   
0 请登录后投票
最后更新时间:2007-05-06
robbin 写道
OneEyeWolf 写道
to robbin:
   ORM, 我现只能看到关系映射与维护这一层,至于OLTP这种类型的应用,我也没有做这样的项目,所以看不到这么远。
   不过在实际的应用中,有二级缓存,总比没有强,但作用多大,我实际上是讲不清楚的。因为在实际的调优过程中,这一项,一直是打开的,并且用的是TreeCache。但瓶颈点不在这个地方,所以感觉效果并不是很明显。也许去掉二级缓存,就能感觉到了。

  


OLTP就是相对于OLAP来说的,联机事务处理,普通的不涉及到大数据量报表分析查询的系统其实都是OLTP,特别是一般的互联网网站都是OLTP。

二级缓存在数据量小访问量小的时候,效果看不出来,当然也需要程序员在开发的时候就要有针对性的设计。但是当数据量大的时候,效果就出来了。一般的程序员在软件交付使用以后,不再继续跟踪这个项目了,所以一般不会涉及到这个层面。因此无论是ORM运用不好造成的严重性能问题,还是Hibernate良好运用对于性能的巨大提升,一般都没有一个清楚的认识。他们只知道通过看到log里面一屏一屏滚过的SQL就觉得Hibernate有问题了,呵呵。

Hibernate的二级缓存是针对对象级别的,即使你打开TreeCache,如果你没有针对具体的对象设置cache选项,一样没有用上。另外就像我在演讲里面提到过的,用不用ORM,用不用缓存,这是一个架构问题,不单纯是你打开不打开TreeCache这么简单,而是涉及到你的整个数据库表结构应该怎么设计,对象关联怎么安排,查询语句怎么写的各方面问题了。

这其实是一个很大的话题,不展开谈了。反正我的观点还是那样,如果对ORM的理解不深刻,对整个架构设计掌控能力不到位的话,那么还是用iBATIS吧。


   我同意Robbin的观点,不是所有的对象,都适合Cache的,也不能拿着Cache,当锤子,把所有的对象,都看成钉子。
    如果一个对象频繁的在cache当中被换进换出,反而降低性能。
   所以就需要分析,需要设计。将不变的,变动少的,和频繁更新的进行剥离。
  
   但如果把调优的寄与Hibernate的二级缓存之上,也许太过于偏执。

   比较能够改善性能的,还是全局缓存,然后才是二级缓存。

   调优也是讲究次序的,关键是性能瓶颈点在什么地方。

   如果要进一步的提高性能,可能是一些商业级的缓存产品,如TangoSol。

   另外以前,有人说过Hibernate的二级缓存打开,在cluster环境下,有影响,这个具体我没有测试过,不知道是不是真的?
   
0 请登录后投票
最后更新时间:2007-05-06
最近也感觉ORM工具不好用,不过没有太多技术因素,主要是在互联网应用中,大量使用的是查询操作,感觉OO的意义并不明显。另外就是人员问题,无论是熟练级别还是精通级别,SQL比HQL容易找到人手。
   
0 请登录后投票
最后更新时间:2007-06-06
引用

我同意Robbin的观点,不是所有的对象,都适合Cache的,也不能拿着Cache,当锤子,把所有的对象,都看成钉子。
    如果一个对象频繁的在cache当中被换进换出,反而降低性能。
   所以就需要分析,需要设计。将不变的,变动少的,和频繁更新的进行剥离。
  
   但如果把调优的寄与Hibernate的二级缓存之上,也许太过于偏执。

   比较能够改善性能的,还是全局缓存,然后才是二级缓存。

   调优也是讲究次序的,关键是性能瓶颈点在什么地方。

这说明你并没有认识到对象缓存的真正作用。并不是说把什么调优寄予Hibernate的二级缓存之上,如果你的应用架构本来就不是针对ORM进行设计的,你再怎么使用二级缓存也没有效果。

缓存是有很多层次的,有web server前端缓存,有动态页面静态化,有页面片断缓存,有查询缓存,也有对象缓存。不同层面的缓存适用于不同的应用场景,作用也各自不同,如果可以,你全部一起用上,他们不矛盾,但这个话题比较大,现在不展开谈。

针对OLTP类型的web应用,只要代码写的质量没有问题,最终的性能瓶颈毫无疑问还是数据库查询。应用服务器层面可以水平扩展,但是数据库是单点的,很难水平扩展,所以如何有效降低数据库查询频率,减轻数据库压力,是web应用性能问题的根源。

以上所有的缓存方式都可以直接或者间接的降低数据库访问,但缓存是有应用场景的,虽然新闻网站非常适合使用动态页面静态化技术,但是例如电子商务网站就不适合动态页面静态化,而页面缓存和查询缓存可以使用的场景也不多。但是对象缓存是所有缓存技术当中适用场景最广泛的,任何OLTP应用,即使实时性要求很高,你也可以使用对象缓存,而且好的ORM实现,对象缓存是完全透明的,不需要你的程序代码进行硬编码。

用不用对象缓存,怎么用对象缓存,不是一个调优的技巧问题,而是整个应用的架构问题。在你开发一个应用之前,你就要想清楚,这个应用最终的场景是什么?会有多大的用户量和数据量。你将采用什么方式来架构这个应用:

OK,也许你偏爱SQL,那么你选择iBATIS,数据库设计当中大表有很多冗余字段,会尽量消除大表之间的关联关系,最终用户量和访问量很高以后,你会选择使用Oracle,雇佣资深的DBA,进行数据库调优和SQL调优,这是大多数公司走的路。

但是我告诉你,你还有另外一条路可以走。你可以选择ORM(不见得一定是Hibernate),数据库设计当中避免出现大表,比较多的表关联关系,通过ORM以对象化方式操作。当用户量和访问量很高以后,除了数据库端本身的优化,你还有对象缓存这条途径。对象缓存是怎样提高性能的呢?随便举个例子:

论坛的列表页面,需要显示topic的分页列表,topic作者的名字,topic最后回复帖子的作者,如果是iBATIS,你准备怎么做?

select ... from topic left join user left join post .....


你需要通过join user表来取得topic作者的名字,然后你还需要join post表取得最后回复的帖子,post再join user表取得最后回贴作者名字。

也许你说,我可以设计表冗余,在topic里面增加username,在post里面增加username,所以通过大表冗余字段,消除了复杂的表关联:

select ... from topic left join post...


OK,且不说冗余字段的维护问题,现在仍然是两张大表的关联查询。然后让我们看看ORM怎么做?

select * from topic where ... --分页条件


就这么一条SQL搞定,比上面的关联查询对数据库的压力小多了。

也许你说,不对阿,作者信息呢?回贴作者信息呢?这些难道不会发送SQL吗?如果发送SQL,这不就是臭名昭著的n+1条问题吗?

你说的对,最坏情况下,会有很多条SQL:
select * from user where id = topic_id...;
....
select * from user where id = topic_id...;

select * from post where id = last_topic_id...;
....
select * from post where id = last_topic_id...;

select * from user where id = post_id...;
....
select * from user where id = post_id...;


事实上何止n+1,根本就是3n+1条SQL了。那你怎么还说ORM性能高呢?

因为对象缓存在起作用,你可以观察到后面的3n条SQL语句全部都是基于主键的单表查询,这3n条语句在理想状况下(比较繁忙的web网站),全部都可以命中缓存。所以事实上只有一条SQL,就是:

select * from topic where ...--分页条件


这条单表的条件查询和iBATIS通过字段冗余简化过后的大表关联查询相比,当数据量大到一定程度以后(十几万条),查询的速度会差至少一个数量级,而且对数据库的压力很小,这就是对象缓存的真正威力!


引用
如果要进一步的提高性能,可能是一些商业级的缓存产品,如TangoSol。

Hibernate二级缓存只是一个缓存接口,可以使用任意Java缓存产品,你完全可以用Tangosol做Hibernate的二级缓存。我还是那句话,如果你的应用不是针对ORM进行设计的,就算你用上Tangosol,也没什么用。

引用
另外以前,有人说过Hibernate的二级缓存打开,在cluster环境下,有影响,这个具体我没有测试过,不知道是不是真的?

那要看你用不用支持cluster的缓存产品了。如果在cluster环境下,你非要用一个不支持cluster的缓存产品,你说有没有问题?
   
0 请登录后投票
论坛首页 Java版

跳转论坛:
JavaEye推荐