hibernate 二级缓存和查询缓存原理和关系「建议收藏」

hibernate 二级缓存和查询缓存原理和关系「建议收藏」二级缓存和查询缓存都相当于一个map。二级缓存缓存的key为id,value为实体对象。一般load(),iterate()使用到二级缓存,list()需要结合查询缓存使用。iterate()和list()区别如下:iterate()不需要开启查询缓存,它首先发出一个sql如”selects.idfromStudents”去数据库把id属性列表取出来,然后再根据id列表一个一

大家好,又见面了,我是你们的朋友全栈君。

二级缓存和查询缓存都相当于一个map。
二级缓存缓存的key为id,value为实体对象。一般load(),iterate()使用到二级缓存,list()需要结合查询缓存使用。iterate()和list()区别如下:
iterate()不需要开启查询缓存,它首先发出一个sql如”select s.id from Student s”去数据库把id属性列表取出来,然后再根据id列表一个一个load(),如果缓存有从缓存取,如果缓存没有就从数据库取:select s.id,s.name,s.classid from     Student s where s.id=?,取出后再存入二级缓存。Iterate总会发出取id列表的语句。
List()需要开启查询缓存,它首先发出一个sql如”select s.id,s.name,s.classid from Student s…”去数据库取出所有相关实体,并将这些实体存入二级缓存,将此sql语句及一些相关信息作为key,id列表作为值,第二次查询这条语句时就会去根据sql语句及相关信息去key里找,如果有就会把id列表取出一个一个load(),接下来就和iterate一样了。List一般只有第一次发发出取实体列表的语句,以后的id列表就会去查询缓存取id列表,不会再发出sql语句。
前提:执行同一hql语句,如:select s from Student s
1.关闭查询缓存,开启二级缓存时:
第二次查询属性时iterate只会发出获取id列表的sql,list会发出和第一次一样的请求实体的sql。
2.开启查询缓存,开启二级缓存
第二次查询属性时iterate只会发出获取id列表的sql,list不发sql。
以上说明iterate只和二级缓存有关,list和二级缓存和查询缓存都有关。

 

 

list 不会使用二级缓存,但是会填充二级缓存,还会使用查询缓存

 

                                                    out join          使用查询缓存二级缓存

 

1.Criteria.list                                  会                      会

2.Query.list                                    不会                   会

3.Query.iterate                              会                      会

4.A先Criteria.list                             会

   B后Query.list                               不会               不会使用A的结果缓存

5.A先Query.list                               不会

   B后Criteria.list                             会                  不会使用A的结果缓存

6.A先Query.iterate                         会

   B后Query.list                               不会               不会使用A的结果缓存

7.A先Query.list                               不会

   B后Query.iterate                         不会                 使用A的结果缓存

8.A先Criteria.list                             会

   B后Query.iterate                                                使用A的结果缓存

9.A先Query.iterate                         会

   B后Criteria.list                             会                 不会使用A的结果缓存

 

 


一、hibernate的二级缓存
如果开启了二级缓存,hibernate在执行任何一次查询的之后,都会把得到的结果集放到缓存中,缓存结构可以看作是一个hash table,key是数据库记录的id,value是id对应的pojo对象。当用户根据id查询对象的时候(load、iterator方法),会首先在缓存中查找,如果没有找到再发起数据库查询。但是如果使用hql发起查询(find, query方法)则不会利用二级缓存,而是直接从数据库获得数据,但是它会把得到的数据放到二级缓存备用。也就是说,基于hql的查询,对二级缓存是只写不读的。
针对二级缓存的工作原理,采用iterator取代list来提高二级缓存命中率的想法是不可行的。Iterator的工作方式是根据检索条件从数据库中选取所有目标数据的id,然后用这些id一个一个的到二级缓存里面做检索,如果找到就直接加载,找不到就向数据库做查询。因此假如iterator检索100条数据的话,最好情况是100%全部命中,最坏情况是0%命中,执行101条sql把所有数据选出来。而list虽然不利用缓存,但是它只会发起1条sql取得所有数据。在合理利用分页查询的情况下,list整体效率高于iterator。
二级缓存的失效机制由hibernate控制,当某条数据被修改之后,hibernate会根据它的id去做缓存失效操作。基于此机制,如果数据表不是被hibernate独占(比如同时使用JDBC或者ado等),那么二级缓存无法得到有效控制。
由于hibernate的缓存接口很灵活,cache provider可以方便的切换,因此支持cluster环境不是大问题,通过使用swarmcache、jboss cache等支持分布式的缓存方案,可以实现。但是问题在于:
1、 分布式缓存本身成本偏高(比如使用同步复制模式的jboss cache)
2、 分布式环境通常对事务控制有较高要求,而目前的开源缓存方案对事务缓存(transaction cache)支持得不够好。当jta事务发生会滚,缓存的最后更新结果很难预料。这一点会带来很大的部署成本,甚至得不偿失。
结论:不应把hibernate二级缓存作为优化的主要手段,一般情况下建议不要使用。
原因如下:
1、 由于hibernate批量操作的性能不如sql,而且为了兼容1.0的dao类,所以项目中有保留了sql操作。哪些数据表是单纯被hibernate独占无法统计,而且随着将来业务的发展可能会有很大变数。因此不宜采用二级缓存。
2、 针对系统业务来说,基于id检索的二级缓存命中率极为有限,hql被大量采用,二级缓存对性能的提升很有限。
3、 hibernate 3.0在做批量修改、批量更新的时候,是不会同步更新二级缓存的,该问题在hibernate 3.2中是否仍然存在尚不确定。
二、hibernate的查询缓存
查询缓存的实现机制与二级缓存基本一致,最大的差异在于放入缓存中的key是查询的语句,value是查询之后得到的结果集的id列表。表面看来这样的方案似乎能解决hql利用缓存的问题,但是需要注意的是,构成key的是:hql生成的sql、sql的参数、排序、分页信息等。也就是说如果你的hql有小小的差异,比如第一条hql取1-50条数据,第二条hql取20-60条数据,那么hibernate会认为这是两个完全不同的key,无法重复利用缓存。因此利用率也不高。
另外一个需要注意的问题是,查询缓存和二级缓存是有关联关系的,他们不是完全独立的两套东西。假如一个查询条件hql_1,第一次被执行的时候,它会从数据库取得数据,然后把查询条件作为key,把返回数据的所有id列表作为value(请注意仅仅是id)放到查询缓存中,同时整个结果集放到class缓存(也就是二级缓存),key是id,value是pojo对象。当你再次执行hql_1,它会从缓存中得到id列表,然后根据这些列表一个一个的到class缓存里面去找pojo对象,如果找不到就向数据库发起查询。也就是说,如果二级缓存配置了超时时间(或者发呆时间),就有可能出现查询缓存命中了,获得了id列表,但是class里面相应的pojo已经因为超时(或发呆)被失效,hibernate就会根据id清单,一个一个的去向数据库查询,有多少个id,就执行多少个sql。该情况将导致性能下降严重。
查询缓存的失效机制也由hibernate控制,数据进入缓存时会有一个timestamp,它和数据表的timestamp对应。当hibernate环境内发生save、update等操作时,会更新被操作数据表的timestamp。用户在获取缓存的时候,一旦命中就会检查它的timestamp是否和数据表的timestamp匹配,如果不,缓存会被失效。因此查询缓存的失效控制是以数据表为粒度的,只要数据表中任何一条记录发生一点修改,整个表相关的所有查询缓存就都无效了。因此查询缓存的命中率可能会很低。
结论:不应把hibernate二级缓存作为优化的主要手段,一般情况下建议不要使用。
原因如下:
1、 项目上层业务中检索条件都比较复杂,尤其是涉及多表操作的地方。很少出现重复执行一个排序、分页、参数一致的查询,因此命中率很难提高。
2、 查询缓存必须配合二级缓存一起使用,否则极易出现1+N的情况,否则性能不升反降
3、 使用查询缓存必须在执行查询之前显示调用Query.setCacheable(true)才能激活缓存,这势必会对已有的hibernate封装类带来问题。
总结
详细分析hibernate的二级缓存和查询缓存之后,在底层使用通用缓存方案的想法基本上是不可取的。比较好的做法是在高层次中(业务逻辑层面),针对具体的业务逻辑状况手动使用数据缓存,不仅可以完全控制缓存的生命周期,还可以针对业务具体调整缓存方案提交命中率。Cluster中的缓存同步可以完全交给缓存本身的同步机制来完成。比如开源缓存swarmcache采用invalidate的机制,可以根据用户指定的策略,在需要的时候向网络中的其他swarmcache节点发送失效消息,建议采用。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/142480.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • ZigBee协议栈简介和流程「建议收藏」

    ZigBee协议栈简介和流程「建议收藏」ZigBee协议栈实际上就是ZigBee协议的API接口一般步骤为:1.组网:调用协议栈的组网函数、加入网络函数,实现网络的建立与节点的加入2.发送:发送节点调用协议栈的无线数据发送函数,实现无线数据发送3.接收:接收节点调用协议栈的无线数据接收函数,实现无线数据接收大致流程:main()→osal_init_system()→osalInitTasks()→SampleAp

  • Mysql表分区(diskgenius分区教程)

    Mysql表分区(diskgenius分区教程)一、MySQL分区表介绍分区是一种表的设计模式,正确的分区可以极大地提升数据库的查询效率,完成更高质量的SQL编程。但是如果错误地使用分区,那么分区可能带来毁灭性的的结果。分区功能并不是在存储引擎层完成的,因此不只有InnoDB存储引擎支持分区,常见的存储引擎MyISAM、NDB等都支持分区。但是并不是所有的存储引擎都支持,如CSV、FEDORATED、MERGE等就不支持分区。在

  • Android SDK下载安装及环境配置[通俗易懂]

    Android SDK下载安装及环境配置[通俗易懂]前面两步,我们已经配置了JDK变量环境,并安装好了Eclipse,通过这两步之后Java的开发环境就准备好了,如果我们只是开发普通的JAVA应用程序的话,那么到这里就可以了。但如果我们要通过Eclip

  • docker dockerfile详解_docker常用命令

    docker dockerfile详解_docker常用命令前言Dockerfile是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。Dockerfile简介Dockerfile是用来构建Docker镜像的构建文件,是由一系列

  • pycharm2021.3激活 3月最新注册码

    pycharm2021.3激活 3月最新注册码,https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • filter过滤器是什么牌子_棉芯过滤器

    filter过滤器是什么牌子_棉芯过滤器ffmeg过滤器介绍[转]  在ffmpeg中,进行反交错需要用到avfilter,即图像过滤器,ffmpeg中有很多过滤器,很强大,反交错的过滤器是yadif. 基本的过滤器使用流程是:         解码后的画面—>buffer过滤器—->其他过滤器—->buffersink过滤器—>处理完的画面所有的过滤器形成了过滤器链,一定要的两…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号