来源:http://www.javaeye.com/topic/431603
0. 前言
由于对Hibernate 的二级缓存和查询缓存的区别不了解,也不知道它们起什么作用。于是动手做了一些实验,对它们的组合使用有了一个表面的认识。
1. 前提
1) 不使用一级缓存(Session 级别)的情况。因为大部分api 对session 的操作都进行n 层封装,用完session 就close ,一般很少使用到一级缓存。
2) 开启hibernate.show_sql=true ,根据sql 的是否输出来判断是否访问了数据库。
2. 基础
2.1. 二级缓存
二级缓存是SessionFactory 级别的全局缓存,它为每个类(或集合)提供缓存。凡是调用二级缓存的查询方法都会从中受益,比如load,list,iterate 等方法(注意,get 和find 不使用缓存,直接访问数据库)。
开启条件:
1)hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider (这里指定了ehcache )
2)hbm.xml 配置<cache usage="read-write"/>
3)ehcache.xml 定义该类的cache
简单地说,二级缓存是用来保存类对象的,并且填充它的所有属性值。
2.2. 查询缓存
查询缓存保存的是查询方法(list 和iterate 等)查询的sql 语句和结果集,跟load 这种装载对象的方法没有关系。也就是说,使用load 方法,查询缓存不理睬。
并且这个结果集只是对象id 列表,对象的其他属性值不保存。例如,list 执行的时候,先根据查询缓存中的sql 语句,获取对象id 列表,然后再load(id) 来取对象。
开启条件:
1) hibernate.cache.use_query_cache=true
2)Query 或者Criteria 使用时,设置setCacheable(true)
简单地说,查询缓存是用来保存sql 执行后获取的对象id 列表。下一次执行同一条sql 时,可直接从查询缓存中获取到对象id 列表。
3. 实验
以下对最常用的load 和list 方法进行二级缓存和查询缓存的组合测试。load 和list 执行的结果,都是返回同一个对象。
3.1. 四种组合
二级缓存和查询缓存的组合,根据开启和不开启两种情况,划分为四种组合(√代表开启,×代表不开启):
组合 | 二级缓存 | 查询缓存 |
1 | √ | × |
2 | × | √ |
3 | √ | √ |
4 | × | × |
3.2.load 实验
对同一个id ,使用load 执行两次,观察sql 的输出现象,也就是判断第二次load 是否访问了数据库。其中√代表输出,×代表未输出:
组合 | 第一次sql | 第二次sql | 结论 |
1 | √ | × | 与查询缓存无关 |
2 | √ | √ | |
3 | √ | × | |
4 | √ | √ |
当开启了二级缓存,load 先从缓存中获取对象,于是组合1 和3 的第二次sql 都未输出,即第二次查询没有访问数据库。
当没有开启二级缓存,load 总是直接访问数据库的。也证明了,查询缓存与load 方法无关。
3.3.list 实验
对同一条sql 语句,使用list 执行两次,观察sql 的输出现象。其中√代表输出,×代表未输出:
组合 | 第一次sql | 第二次sql | 结论 |
1 | √ | √ | list 每次访问数据库都会填充二级缓存,相当于二级缓存无效 |
2 | √ | √(不一样) | 这两条sql 语句不一样,第一条是list 的,第二条是load 的 |
3 | √ | × |
|
4 | √ | √ |
|
对组合1 现象的解释:
当没有开启查询缓存时,list 每次都会直接访问数据库,然后把二级缓存给重新填充。也就是说, list 在不开启查询缓存的情况下,根本就没有利用到二级缓存的好处。
对组合2 现象的解释:
第一条sql 是list 正常访问数据库的,但第二次执行list 时,由于有了查询缓存,直接根据第一条sql 获取对象的id ,此时由于没有开启二级缓存,load(id) 后也没法从二级缓存中拿数据,只好再次访问数据库。但在组合3 中,load(id) 可以从二级缓存中拿数据,所以不用访问数据库。
3.4. 小结
在load 和list 方法中,使用二级缓存和查询缓存的不同组合,效果出现了差别。特别强调的是,由于很多操作是基于list 的,如果没有开启查询缓存,根本就使用不了二级缓存的数据,也就提高不了性能了(但是iterate 可以使用到二级缓存)。
另外,从表格中可以看出来,同时开启二级缓存和查询缓存,效果是最好的,第二次查询都不需要访问数据库。
4. 维护
当存在除hibernate 之外的改变数据库数据的方式时,维护二级缓存和查询缓存是很麻烦的。比如使用jdbc 或者第三方系统修改了数据库,hibernate 都不知道,也就没法自行维护缓存了。这时需要手动维护,如果存在第三方系统,还需要让第三方系统发消息通知hibernate 。
维护手段也就是evict (清除某个类的二级缓存)和evictQuery (清除查询缓存)。针对数据修改的三种形式,分别维护:
1)insert :evictQuery
2)delete :evict 和evictQuery
3)update :evict