ImageVerifierCode 换一换
格式:DOCX , 页数:18 ,大小:30.63KB ,
资源ID:9023487      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/9023487.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(为什么ORM性能比iBATIS好doc.docx)为本站会员(b****7)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

为什么ORM性能比iBATIS好doc.docx

1、为什么ORM性能比iBATIS好doc缓存是有很多层次的,有web server前端缓存,有动态页面静态化,有页面片断缓存,有查询缓存,也有对象缓存。不同层面的缓存适用于不同的应用场景,作用也各自不同,如果可以,你全部一起用上,他们不矛盾,但这个话题比较大,现在不展开谈。 针对OLTP类型的web应用,只要代码写的质量没有问题,最终的性能瓶颈毫无疑问还是数据库查询。应用服务器层面可以水平扩展,但是数据库是单点的,很难水平扩展,所以如何有效降低数据库查询频率,减轻数据库压力,是web应用性能问题的根源。 以上所有的缓存方式都可以直接或者间接的降低数据库访问,但缓存是有应用场景的,虽然新闻网站非常

2、适合使用动态页面静态化技术,但是例如电子商务网站就不适合动态页面静态化,而页面缓存和查询缓存可以使用的场景也不多。但是对象缓存是所有缓存技术当中适用场景最广泛的,任何OLTP应用,即使实时性要求很高,你也可以使用对象缓存,而且好的ORM实现,对象缓存是完全透明的,不需要你的程序代码进行硬编码。 用不用对象缓存,怎么用对象缓存,不是一个调优的技巧问题,而是整个应用的架构问题。在你开发一个应用之前,你就要想清楚,这个应用最终的场景是什么?会有多大的用户量和数据量。你将采用什么方式来架构这个应用: OK,也许你偏爱SQL,那么你选择iBATIS,数据库设计当中大表有很多冗余字段,会尽量消除大表之间的

3、关联关系,最终用户量和访问量很高以后,你会选择使用Oracle,雇佣资深的DBA,进行数据库调优和SQL调优,这是大多数公司走的路。 但是我告诉你,你还有另外一条路可以走。你可以选择ORM(不见得一定是Hibernate),数据库设计当中避免出现大表,比较多的表关联关系,通过ORM以对象化方式操作。当用户量和访问量很高以后,除了数据库端本身的优化,你还有对象缓存这条途径。对象缓存是怎样提高性能的呢?随便举个例子: 论坛的列表页面,需要显示topic的分页列表,topic作者的名字,topic最后回复帖子的作者,如果是iBATIS,你准备怎么做? Sql代码 1. select.fromtopi

4、cleftjoinuserleftjoinpost.select . from topic left join user left join post .你需要通过join user表来取得topic作者的名字,然后你还需要join post表取得最后回复的帖子,post再join user表取得最后回贴作者名字。 也许你说,我可以设计表冗余,在topic里面增加username,在post里面增加username,所以通过大表冗余字段,消除了复杂的表关联: Sql代码 1. select.fromtopicleftjoinpost.select . from topic left join

5、post.OK,且不说冗余字段的维护问题,现在仍然是两张大表的关联查询。然后让我们看看ORM怎么做? Sql代码 1. select*fromtopicwhere.-分页条件select * from topic where . -分页条件就这么一条SQL搞定,比上面的关联查询对数据库的压力小多了。 也许你说,不对阿,作者信息呢?回贴作者信息呢?这些难道不会发送SQL吗?如果发送SQL,这不就是臭名昭著的n+1条问题吗? 你说的对,最坏情况下,会有很多条SQL: Sql代码 1. select*fromuserwhereid=topic_id.; 2. . 3. select*fromuser

6、whereid=topic_id.; 4. 5. select*frompostwhereid=last_topic_id.; 6. . 7. select*frompostwhereid=last_topic_id.; 8. 9. select*fromuserwhereid=post_id.; 10. . 11. select*fromuserwhereid=post_id.;select * from user where id = topic_id.;.select * from user where id = topic_id.;select * from post where id

7、 = 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,就是: Sql代码 1. select*fromtopic

8、where.-分页条件select * from topic where .-分页条件这条单表的条件查询和iBATIS通过字段冗余简化过后的大表关联查询相比,当数据量大到一定程度以后(十几万条),查询的速度会差至少一个数量级,而且对数据库的压力很小,这就是对象缓存的真正威力! 更进一步分析,使用ORM,我们不考虑缓存的情况,那么就是3n+1条SQL。但是这3n+1条SQL的执行速度一定比iBATIS的大表关联查询慢吗?不一定!因为使用ORM的情况下,第一条SQL是单表的条件查询,在有索引的情况下,速度很快,后面的3n条SQL都是单表的主键查询,在繁忙的数据库系统当中,3n条SQL几乎可以全部命

9、中数据库的data buffer。但是使用iBATIS的大表关联查询,很可能会造成全表扫描,这样性能是非常差的。 所以结论就是:即使不使用对象缓存,ORM的n+1条SQL性能仍然很有可能超过iBATIS的大表关联查询,而且对数据库造成的压力要小很多。这个结论貌似令人难以置信,但经过我的实践证明,就是事实。前提是数据量和访问量都要比较大,否则看不出来这种效果 还是拿上面这个例子的应用场景来说,由于JavaEye网站用RoR的ActiveRecord,所以这个场景事实上就会发送3n+1条SQL语句。我从log里面看到这密密麻麻的SQL,着实非常担忧性能,所以尝试使用了find的:include选项

10、去eager fetch,迫使ActiveRecord发送单条复杂的关联查询。但非常不幸的是,在网站服务器的production.log里面经过前后对比,发现使用:include以后,单条复杂关联查询耗时更多,数据库压力更大。 在使用memcached之后,比3n+1条的性能进一步明显提升。所以性能对比就是这样的: ORM + Cache ORM n+1 iBATIS 关联查询 那为什么应用Cache可以进一步提高性能,是因为访问Cache的开销比访问数据库小的得多造成的。 应用程序根据主键key去Cache Server取value,是非常简单的算法,开销极小。 而发送一条主键查询的SQL到

11、数据库,要经过非常复杂的过程,有SQL的解析,执行计划的优化,占位符参数的代入,只读事务的保护和隔离等等,最终虽然也命中了数据库的data buffer,但是开销确实很大。 BerkeleyDB就是一个极好的证明,它号称其查询速度是Oracle的1000倍,不是因为它做的比Oracle牛,而是因为它本质上就是一个大Cache,查询没有额外的开销。 /ORM缓存策略 ORM的数据缓存策略分为三个层次:.事务级缓存(Transaction Layer Cache)在当前事务范围内的数据缓存策略,这个事务可能是一个数据库事务,也可能是某个应用级事务.对于hibernate而言,事务级缓存是基于ses

12、sion的生命周期而存在的,每个session实例都会维护一个数据缓存,此缓存随着session的创建(销毁)而存在(消亡).因此也称为Session Level Cache.2.应用级/进程级缓存(Application/Process Layer Cache)在某个应用中,或者应用中某个数据访问子集中的共享缓存.此缓存可由多个事务(数据库事务或应用级事务)共享.对于hibernate而言,应用级缓存在SessionFactory层实现,由这个SessionFactory创建的session实例共享此缓存.因此也称为SessionFactory Level Cache.但在多实例并发运行的环

13、境中不能使用应用级缓存.3.分布式缓存(Cluster Layer Cache)在多个应用实例,多个JVM之间共享的缓存模式.由多个应用级缓存实例组成集群,通过某种远程机制(如RMI或JMS)实现各个缓存实例间的数据同步,任何一个实例的数据修改操作,将导致整个集群间的数据状态同步./最近做一个比较大的电子商务项目,预计每天订单量将在万多单,客服人员需要频繁的下单、查询订单、操作订单,客人预订完订单后,会立即进入处理流程,为了提高服务质量,要求流水化作业,平均要在分钟分钟内处理完订单,对于疑难订单要到第二天,才能处理完。所以订单在创建后,会在短时间内,被频繁的修改和查看。由于在项目中ORM层主要

14、是基于Hibernate框架,所以在调优时,很自然的就想到打开Hibernate的二级缓存,以次来减小由于Load订单大对象时N+1次查询给数据造成的压力,自己做的测试效果也非常好,也顺利通过压力测试。但在上线时,性能却并不佳,经过分析业务的操作特点,查找原因有以下几占:, 1.但由于中台每天在工作当中,频繁的批量分配工单, 因为要批量将订单分配给某一个工作人员处理,在代码当中执行了一个bulkUpdate的操作:template.bulkUpdate(update order set owner = ? where id in (?, ?,?); 这时Hibernate会直接将Order对象

15、的二级缓存清楚掉。 由于二级缓存,总是被刷掉,再查询时,需要重新从数据库Load,所以二级缓存变相直接起的作用很少。 2.由于工作人员在处理订单时,每一次查看之后,都有更新操作,在更新之后,订单被清除缓存,下一组人在处理订单时,又得重新LOAD,所以效果并不好。 3.无论是白盒测试,还是压力测试时,所基于的案例太过于简单,没有更深入的模仿业务操作,对于压力测试的脚本,也很难精确的模拟出真实的流程化的业务操作过程。 开始想到,直接获得Session,直接使用JDBC来编写更新代码,并在更新后,使用sessionFactory.evict(Order.class, id);来有目标的逐个清除特定的

16、对象,以避免全部清楚缓存。 但样做,会对DAO层,修改过大。 由于整个模块最核心的商业对象就是订单,最后决定在Service层对订单开小灶,对订单缓存的单独的定制处理。 我觉得应用缓存存在以下优点: 。速度要快于ORM缓存, 。对于缓存的控制权更大,可以直接控制缓存工具的API进行操作,可以避免一些盲目清除的操作。 。更灵活的控制缓存中对象的失效,如根据事件来清除缓存,如订单的处理流程结束时,将该订单从缓存中清除掉, 。在更新数据库时,不是直接清除缓存,而是更新缓存(尽管这有风险),当业务层抛出异常时,才去清空缓存,避免由于频繁更新,而清空缓存。 缺点: 。订单的更新操作,必须是单点的,只能通

17、过IOrderService提供的接口,进行更新操作,否则数据不一致的风险较大。 。想要透明化,需要有一定的代码工作量,不容易达到ORM缓存最强大的那种透明化和灵活可配置,你可以使用Ehcache, 也可以选Jboss,有钱的话,可以用Tangosol。 。如果不对第三方缓存包,进行一定的封装的话,会直接耦合于第三方的缓存包,不能像Hibernate那样,灵活选择和配置缓存工具。 。对业务层代码有一定的侵 目前的方案是采用应用层的现代化,同时使用如Proxy模式来提供透明化的设计, IOrderService OrderService CacheableOrderService 通过Sprin

18、g的Bean配置,一样可以实现透明化的操作。 结论: 。缓存的清空与更新,要尽量精确的去操作受到更新影响的对象,而不是全部搞掉。 在Hibernate当中,也提供了sessionFactory.evict(class, id)这样细粒度的清空缓存对象的方法。 sessionFactory.evice(class)的操作,要看这样的操作是否频繁,如果频繁,对于缓存的作用就会大大的折扣。如果缓存对象过多,对于失效的算法与处理,要与业务对象的特性紧密的联合起来,通过事件来驱动对象的失效。对于商业对象的缓存,必须要深刻分析对象的生命周期,业务特性。对于数据不一致的风险,要有足够的认识与预防手段。合理的

19、估计订单对象的大小,分配足够的内存。如果只使用中心缓存,只能减小数据库的压力,对于网络带宽的压力,还是有的,速度上也远远逊于本地缓存的效果,所以要结合本地缓存中心缓存的策略方案,即提高速度,避免群集复制时的瓶颈。/相关文章: Hibernate缓存机制 细谈Ehcache页面缓存的使用 怎样判断对象是否存在于缓存中 推荐圈子: Hibernate 更多相关推荐 很多人对二级缓存都不太了解,或者是有错误的认识,我一直想写一篇文章介绍一下hibernate的二级缓存的,今天终于忍不住了。 我的经验主要来自hibernate2.1版本,基本原理和3.0、3.1是一样的,请原谅我的顽固不化。 hibe

20、rnate的session提供了一级缓存,每个session,对同一个id进行两次load,不会发送两条sql给数据库,但是session关闭的时候,一级缓存就失效了。 二级缓存是SessionFactory级别的全局缓存,它底下可以使用不同的缓存类库,比如ehcache、oscache等,需要设置hibernate.cache.provider_class,我们这里用ehcache,在2.1中就是 hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider 如果使用查询缓存,加上 hibernate.cache.

21、use_query_cache=true 缓存可以简单的看成一个Map,通过key在缓存里面找value。 Class的缓存 对于一条记录,也就是一个PO来说,是根据ID来找的,缓存的key就是ID,value是POJO。无论list,load还是iterate,只要读出一个对象,都会填充缓存。但是list不会使用缓存,而iterate会先取数据库select id出来,然后一个id一个id的load,如果在缓存里面有,就从缓存取,没有的话就去数据库load。假设是读写缓存,需要设置: <cache usage=read-write/> 如果你使用的二级缓存实现是ehcache的话

22、,需要配置ehcache.xml <cache name=com.xxx.pojo.Foo maxElementsInMemory=500 eternal=false timeToLiveSeconds=7200 timeToIdleSeconds=3600 overflowToDisk=true /> 其中eternal表示缓存是不是永远不超时,timeToLiveSeconds是缓存中每个元素(这里也就是一个POJO)的超时时间,如果eternal=false,超过指定的时间,这个元素就被移走了。timeToIdleSeconds是发呆时间,是可选的。当往缓存里面put的元素超

23、过500个时,如果overflowToDisk=true,就会把缓存中的部分数据保存在硬盘上的临时文件里面。 每个需要缓存的class都要这样配置。如果你没有配置,hibernate会在启动的时候警告你,然后使用defaultCache的配置,这样多个class会共享一个配置。 当某个ID通过hibernate修改时,hibernate会知道,于是移除缓存。 这样大家可能会想,同样的查询条件,第一次先list,第二次再iterate,就可以使用到缓存了。实际上这是很难的,因为你无法判断什么时候是第一次,而且每次查询的条件通常是不一样的,假如数据库里面有100条记录,id从1到100,第一次li

24、st的时候出了前50个id,第二次iterate的时候却查询到30至70号id,那么30-50是从缓存里面取的,51到70是从数据库取的,共发送1+20条sql。所以我一直认为iterate没有什么用,总是会有1+N的问题。 (题外话:有说法说大型查询用list会把整个结果集装入内存,很慢,而iterate只select id比较好,但是大型查询总是要分页查的,谁也不会真的把整个结果集装进来,假如一页20条的话,iterate共需要执行21条语句,list虽然选择若干字段,比iterate第一条select id语句慢一些,但只有一条语句,不装入整个结果集hibernate还会根据数据库方言做

25、优化,比如使用mysql的limit,整体看来应该还是list快。) 如果想要对list或者iterate查询的结果缓存,就要用到查询缓存了 查询缓存 首先需要配置hibernate.cache.use_query_cache=true 如果用ehcache,配置ehcache.xml,注意hibernate3.0以后不是net.sf的包名了 <cache name=net.sf.hibernate.cache.StandardQueryCache maxElementsInMemory=50 eternal=false timeToIdleSeconds=3600 timeToLive

26、Seconds=7200 overflowToDisk=true/> <cache name=net.sf.hibernate.cache.UpdateTimestampsCache maxElementsInMemory=5000 eternal=true overflowToDisk=true/> 然后 query.setCacheable(true);/激活查询缓存 query.setCacheRegion(myCacheRegion);/指定要使用的cacheRegion,可选 第二行指定要使用的cacheRegion是myCacheRegion,即你可以给每个查询缓

27、存做一个单独的配置,使用setCacheRegion来做这个指定,需要在ehcache.xml里面配置它: <cache name=myCacheRegion maxElementsInMemory=10 eternal=false timeToIdleSeconds=3600 timeToLiveSeconds=7200 overflowToDisk=true /> 如果省略第二行,不设置cacheRegion的话,那么会使用上面提到的标准查询缓存的配置,也就是net.sf.hibernate.cache.StandardQueryCache 对于查询缓存来说,缓存的key是根据

28、hql生成的sql,再加上参数,分页等信息(可以通过日志输出看到,不过它的输出不是很可读,最好改一下它的代码)。 比如hql: from Cat c where c.name like ? 生成大致如下的sql: select * from cat c where c.name like ? 参数是tiger%,那么查询缓存的key*大约*是这样的字符串(我是凭记忆写的,并不精确,不过看了也该明白了): select * from cat c where c.name like ? , parameter:tiger% 这样,保证了同样的查询、同样的参数等条件下具有一样的key。 现在说说缓存

29、的value,如果是list方式的话,value在这里并不是整个结果集,而是查询出来的这一串ID。也就是说,不管是list方法还是iterate方法,第一次查询的时候,它们的查询方式很它们平时的方式是一样的,list执行一条sql,iterate执行1+N条,多出来的行为是它们填充了缓存。但是到同样条件第二次查询的时候,就都和iterate的行为一样了,根据缓存的key去缓存里面查到了value,value是一串id,然后在到class的缓存里面去一个一个的load出来。这样做是为了节约内存。 可以看出来,查询缓存需要打开相关类的class缓存。list和iterate方法第一次执行的时候,都是既填充查询缓存又填充class缓存的。 这里还有一个很容易被忽视的重要问题,即打开查询缓存以后,即使是list方法也可能遇到1+N的问题!相同条件第一次list的时候,因为查询缓存中找不到,不管class缓存是否存在数据,总是发送一条sql语句到数据库获取全部数据,然后填充查询缓存和class缓存。但是第二次执行的时候,问题就来了,如果你的class缓存的超时时间比较短,现在class缓存都超时了,但是查询缓存还在,那么list方法在获取id串以后,将会一个一个去数据库load!因此,class缓存的超时时间一定不能短于查询缓存设置的超时时间!如果还设置了发

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1