得,今天来聊聊我在项目里捣鼓Hibernate二级缓存那点事儿。
项目跑起来总感觉有点慢,特别是有些数据老是重复去数据库里捞。Hibernate自带那个一级缓存,也就是Session级别的,确实能顶一阵子,同一个Session里重复查同一个ID的对象,它能直接从内存里给你,挺快。但问题是,这玩意儿生命周期太短,跟Session一样,一次请求或者说一个事务结束,它就没。下次再来,该查数据库还得查,对于那种好多用户都会频繁访问的热点数据,效果就不明显。
我就琢磨着,得搞个更持久点的缓存,能跨Session共享的。这时候就想到Hibernate的二级缓存。这东西是跟SessionFactory绑定的,只要SessionFactory活着,缓存就可能在,不同的Session都能用,听起来就很对路。
动手搞起
第一步:选家伙事儿
Hibernate自己不直接实现二级缓存的具体存储,得找个第三方库来干这活。网上瞅瞅,Ehcache用的比较多,名气也大,就决定用它。得,先往项目里加依赖,用Maven的话就在`*`里加上Ehcache和Hibernate对Ehcache支持的那个包。
第二步:开开关
光加依赖不行,得告诉Hibernate:“老哥,我要用二级缓存,用Ehcache这个牌子的!”
这个得在Hibernate的配置文件里(我用的是`*`,用JPA的话就是`*`)加上几个关键设置:
*_second_level_cache
这个肯定得设成 `true`,不然白搭。*.factory_class
这个就指定用哪个缓存提供商的工厂类,用Ehcache的话就填Ehcache对应的那个类名(具体名字得查一下,不同版本可能微调)。- (可选)
*_query_cache
这个是查询缓存,跟二级缓存(主要缓存实体对象)还不太一样,如果想缓存HQL查询结果也要把它设成`true`。
第三步:细调Ehcache
光打开开关还不够,Ehcache自己也得配置一下。一般需要在项目的`classpath`下放一个`*`文件。这里面可以定义默认的缓存策略,比如缓存多少对象、缓存多久、内存不够怎么办(是写到硬盘还是直接扔掉)等等。还可以给不同的实体类或者集合定义专门的缓存区域(Region),进行更精细的控制。
我当时就定义一个默认的缓存策略,然后给几个特别常用的实体类单独配置下,比如用户、产品信息这种,让它们缓存时间长一点,数量多一点。
第四步:标记要缓存的实体
配置好,Hibernate和Ehcache都知道要干活,但具体哪些实体类的数据要放进二级缓存?这得在实体类上做标记。
我用的是注解方式,在实体类上加上`@Cacheable` 和 `@Cache`注解。`@Cache`里面要指定缓存的并发访问策略(`usage`属性),这个挺重要。有几种策略:
READ_ONLY
:只读数据,基本不变的,性能最NONSTRICT_READ_WRITE
:非严格读写,偶尔更新,不要求强一致性,性能也不错。READ_WRITE
:读写数据,需要保证事务隔离性,性能稍差,但最常用。TRANSACTIONAL
:完全事务化,性能开销最大。
我根据实际情况,大部分常用但偶尔会改的数据用`READ_WRITE`,一些基础配置类数据用`READ_ONLY`。
第五步:跑起来看看
一通配置注解加完,重启项目,开始测试。重点观察日志,Hibernate和Ehcache的日志级别调高点,能看到很多缓存相关的信息,比如缓存是否命中(hit)、是否未命中(miss)、往缓存里放什么(put)等等。
用压力测试工具或者手动多刷新几次页面,看看之前慢的地方是不是快,数据库的查询压力是不是真的降下来。
实践中的体会
用上二级缓存后,效果还是挺明显的,特别是那些根据ID反复查询单个对象的操作,速度提升很大,数据库压力也小不少。Ehcache的表现也挺稳定。
不过也踩些坑:
- 配置有点烦:又是Hibernate配置,又是Ehcache配置,还得给实体加注解,一套下来步骤不少,哪一步错都不行。
- 缓存更新问题:用`READ_WRITE`策略,Hibernate会自动处理缓存更新和失效,但如果绕过Hibernate直接用SQL改数据库,缓存是不知道的,可能导致数据不一致。这点要特别注意,尽量通过Hibernate的API来操作数据。
- 不是万能药:二级缓存主要解决的是按ID查单个对象或者加载关联对象(如果也配置缓存)的场景。对于复杂的条件查询,它的作用有限,这时候得靠查询缓存(Query Cache),但查询缓存坑更多,更容易失效,我一般比较谨慎使用。
- 内存占用:缓存是要占内存的,得根据服务器资源合理配置缓存大小和策略,不然可能导致内存溢出。
Hibernate二级缓存是个好东西,尤其是在读多写少的场景下,对提升性能、减轻数据库负担很有帮助。但它也不是银弹,需要仔细配置和理解其工作原理,特别是缓存策略和数据一致性问题,得想清楚再用。我这套搞下来,虽然折腾一阵子,但效果达到预期,也算是值。