前言
springboot中我们最常用的缓存数据库可能是redis、memcached等。但是,redis适合的场景是“小数据,高频访问”,有一些场景则不适合。比如你想把你的CMS系统、博客系统提速,做全站缓存。这时候你需要一个可以保存数据到服务器本地内存、磁盘的缓存工具。本文推荐使用EhCache来完成“大小数据、高频访问”的场景需求。
相比Redis,EhCache对数据的存取都在服务器本机,所以没有网络开销。但缺点也很明显,就是服务器的内存大小、磁盘大小决定了EhCache能够存放多少数据。还有个缺点,就是在分布式环境下,因为缓存保存在本机,所以无法与集群内的其他机器共享。(针对上面提到的CMS系统静态化页面,其实CDN是更好的解决方案)
在Springboot中引入EhCache
一、maven引入相关依赖包
-
引入EhCache包
<dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.10.9.2</version> </dependency>
-
引入Springboot cache包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
二、配置Springboot application.properties
需在在springboot的配置文件application.properties中指定使用EhCache作为缓存,并指定EhCache的配置文件所在路径。代码如下:
spring.cache.type=ehcache
spring.cache.ehcache.config=classpath:ehcache.xml
三、@EnableCaching开启Springboot的缓存
在Springboot项目的Main函数上,标记@EnableCaching
注解,开启缓存。如下图所示:
四、配置EhCache配置文件ehcache.xml
在Springboot项目的resources
目录下,新建一个名为ehcache.xml
的配置文件,配置ehcache。内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- 磁盘缓存文件的路径 -->
<diskStore path="./tmp/Ehcache"/>
<!--
默认的缓存配置:
该配置将应用于 通过CacheManager.add(String cacheName)
以编程方式创建的缓存
-->
<defaultCache eternal="true"
maxElementsInMemory="1"
maxElementsOnDisk="0"
overflowToDisk="true"
diskPersistent="true">
</defaultCache>
<cache name="indexArticleList"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
maxElementsInMemory="3"
maxElementsOnDisk="0"
overflowToDisk="true"
diskPersistent="true">
</cache>
<cache name="articleDetails"
eternal="false"
timeToIdleSeconds="36000"
timeToLiveSeconds="36000"
maxElementsInMemory="5"
maxElementsOnDisk="0"
overflowToDisk="true"
diskPersistent="true">
</cache>
<cache name="websiteConfig"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
maxElementsInMemory="30"
maxElementsOnDisk="0"
overflowToDisk="true"
diskPersistent="true">
</cache>
</ehcache>
代码介绍:
- 每一个
<cache></cache>
标签,都是一个缓存单元。后续你在springboot中使用@Cacheable
、@CachePut
等注解时,注解内的value、name就是这里cache标签的name。 - diskStore是磁盘缓存文件路径
具体参数:
name
:缓存名称maxElementsInMemory
:内存中最大缓存对象数maxElementsOnDisk
:硬盘中最大缓存对象数,若是0表示无穷大eternal
:true表示对象永不过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为falseoverflowToDisk
:true表示当内存缓存的对象数目达到了maxElementsInMemory界限后,会把溢出的对象写到硬盘缓存中。注意:如果缓存的对象要写入到硬盘中的话,则该对象必须实现了Serializable接口才行。diskSpoolBufferSizeMB
:磁盘缓存区大小,默认为30MB。每个Cache都应该有自己的一个缓存区。diskPersistent
:是否在VM重启时存储硬盘的缓存数据。默认值是false。diskExpiryThreadIntervalSeconds
:磁盘失效线程运行时间间隔,默认为120秒timeToIdleSeconds
: 设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,如果处于空闲状态的时间超过了timeToIdleSeconds属性值,这个对象就会过期,EHCache将把它从缓存中清空。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地处于空闲状态timeToLiveSeconds
:设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存中后,如果处于缓存中的时间超过了 timeToLiveSeconds属性值,这个对象就会过期,EHCache将把它从缓存中清除。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地存在于缓存中。timeToLiveSeconds必须大于timeToIdleSeconds属性,才有意义memoryStoreEvictionPolicy
:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
常用场景:
- 数据存储到内存和硬盘中,当springboot重启时,内存中的数据要转移到硬盘中保留:配置
diskPersistent
为true、overflowToDisk
为true,当内存中的数据超过maxElementsInMemory
后,就保存到硬盘中。重启springboot后数据仍在硬盘中。 - 数据只存储在硬盘:设置
maxElementsInMemory
为1即可
五、在springboot中通过@Cacheable、@CachePut等使用EhCache缓存
经过上面的步骤配置好EhCache后,你就可以使用springboot自身的缓存注解来调用EhCache了。网上关于springboot常用的几个缓存注解介绍很多,这里不做详细介绍了,简介一下:
@Cacheable
:先检查缓存是否存在,不存在的话,执行被注解的函数。结果再更新到缓存。@CachePut
:不管缓存是否存在,都执行被注解的函数,然后把结果更新到缓存。
六、常见错误
- 所有要被缓存的类,包括类里面的子类,都得实现
Serializable
接口。 - EhCache磁盘缓存目录要有写权限。
- 从EhCache中取出的复杂Object对象,如果后续要修改,请先clone一份再修改,否则该修改会被回写EhCache(该问题刚遇到,还没深入了解)。
疑难杂症
作者在使用EhCache的过程中,发现如果遇到意外断电等特殊情况,EhCache保存在磁盘中的数据有一定概率无法再次访问。通过研究发现,数据本身还是存在的,出问题的是EhCache数据目录中的index文件。通过网上查找资料,说是在springboot程序启动的时候,设置一个shutdown ehcache的参数即可,代码如下:
System.setProperty(net.sf.ehcache.CacheManager.ENABLE_SHUTDOWN_HOOK_PROPERTY, "true");
作者正在测试该设置是否有用,后续更新在这里。
2021-12-16更新:作者实测,以上配置可以解决EhCache意外停止导致的数据丢失问题。