springboot:缓存不止redis,学会使用本地缓存ehcache

这篇具有很好参考价值的文章主要介绍了springboot:缓存不止redis,学会使用本地缓存ehcache。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

0. 引言

随着redis的普及,更多的同学对redis分布式缓存更加熟悉,但在一些实际场景中,其实并不需要用到redis,使用更加简单的本地缓存即可实现我们的缓存需求。

今天,我们一起来看看本地缓存组件ehcache

1. ehcache简介

1.1 简介

ehcache是基于java开发的本地缓存组件,无需单独安装部署,只要引入jar包就可利用它来实现缓存。

所谓本地缓存,就是指存储在JVM堆内存中的临时缓存数据,当然ehcache本身也支持Off-Heap Store机制来使用堆外内存,本地缓存相较于redis性能和响应速度更高。

Ehcache的本地缓存还支持过期时间、最大容量、持久化等特性,使得它可以适用于各种不同的缓存场景。

官方文档地址:https://www.ehcache.org/documentation/

1.2 本地缓存与redis的区别

本地缓存与redis的区别在于:

  • 架构:

    本地缓存基于单机架构,即数据仅本机可用,无法共享给其他服务。除非使用服务调用来获取。而redis本身基于分布式架构,支持跨服务调取。
    所以当数据需要分布式调用时,则适用于redis,如果数据只需要本地获取,则可考虑本地缓存

  • 性能:

    本地缓存本身基于本机内存,没有网络IO消耗,所以性能上大大高于redis,但是如果数据量较大,则还是要考虑使用redis,本地缓存仅适用于数据量小、结构简单的数据场景,不适合复杂的业务数据

  • 功能拓展:

    redis支持持久化、订阅模式、集群、主从模式等,而ehcache更倾向于简单的缓存功能场景,虽然也支持持久化,但是本身并不建议用它来做大型或复杂场景的缓存。如果场景比较简单轻量,对延迟有较高要求,则可选择本地缓存

2. ehcache使用

1、创建一个springboot项目,这里我的springboot版本为2.6.13

2、引入ehcahe组件依赖

这里需要注意的是net.sf.ehcache是ehcache2.X 与 org.ehcache是echcache3.X,两个版本配置有区别

        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.10.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
            <version>2.6.13</version>
        </dependency>

3、在启动类上添加@EnableCaching注解,开启缓存

@SpringBootApplication
@EnableCaching
public class LocalCacheDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(LocalCacheDemoApplication.class, args);
    }

}

4、在配置文件application.yml中添加配置

spring:
  profiles: 
  	active: dev
  cache:
    type: ehcache
    ehcache:
      config: classpath:ehcache.xml

5、在resources文件夹下创建配置文件ehcache.xml,注意这里单独创建了一个name为user的缓存,用于后续保存用户信息缓存。如果有不同的缓存需要使用不同的name的,需要单独创建cache标签

标签介绍:

defaultCache: 默认缓存配置标签
cache 指定缓存标签,name表示缓存名称
diskStore 数据存储磁盘路径

属性介绍:

eternal: 缓存是否永久有效,如果为 true 则忽略timeToIdleSeconds 和 timeToLiveSeconds
maxElementsInMemory:最多缓存多少个key
overflowToDisk: 缓存超限时是否写入磁盘,默认为true
overflowToOffHeap: 堆内存超限时是否使用堆外内存,企业版功能,收费
diskPersistent:缓存是否持久化
timeToLiveSeconds:缓存多久过期
timeToIdleSeconds:缓存多久没有被访问就过期
diskExpiryThreadIntervalSeconds:磁盘缓存过期检查线程运行时间间隔
memoryStoreEvictionPolicy:缓存淘汰策略, LFU:最近最少使用的元素先移出; FIFO:最先进入的元素被移出; LRU:使用越少的元素被移出
maxBytesLocalHeap:缓存最大占用JVM堆内存,0表示不限制,单位支持K、M或G
maxBytesLocalOffHeap: 缓存最大占用堆外内存,0表示不限制,单位支持K、M或G,企业版功能,收费
maxBytesLocalDisk:缓存最大占用磁盘,0表示不限制,单位支持K、M或G

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
         
    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToLiveSeconds="3600"
            timeToIdleSeconds="0"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="user"
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToLiveSeconds="3600"
            timeToIdleSeconds="0"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"/>

<!--    存储到磁盘时的路径-->
    <diskStore path="/Users/wuhanxue/Downloads/ehcache" />

</ehcache>

6、缓存使用,在获取方法中使用@Cacheable注解,在更新方法中使用@CachePut注解。
我这里模拟就没有访问数据库查询数据了,大家在实际书写的时候可以连接上数据源测试

@RestController
@RequestMapping("user")
public class UserController {

    @GetMapping("get")
    @Cacheable(cacheNames = "user", key = "#id")
    public User getById(Integer id) {
        System.out.println("get第一次获取,不走缓存");
        User user = new User();
        user.setId(id);
        user.setAge(18);
        user.setName("benjamin_"+id);
        user.setSex(true);
        return user;
    }

    @PostMapping("update")
    @CachePut(cacheNames = "user", key = "#search.id")
    public User update(@RequestBody User search) {
        System.out.println("update更新缓存");
        User user = new User();
        Integer id = search.getId();
        user.setId(id);
        user.setAge(search.getAge() != null ? search.getAge()+1 : 0);
        user.setName("update_benjamin_"+id);
        user.setSex(true);
        return user;
    }

}

3. 测试

1、调用查询接口:localhost:8080/user/get?id=1

springboot:缓存不止redis,学会使用本地缓存ehcache

2、第一次调用,打印"get第一次获取,不走缓存"。再调用一次发现没有打印了,但是数据正常查询,说明走了缓存

springboot:缓存不止redis,学会使用本地缓存ehcache

3、调用更新接口

springboot:缓存不止redis,学会使用本地缓存ehcache

4、再调用查询接口,查询到的就是更新的数据,说明缓存更新成功

springboot:缓存不止redis,学会使用本地缓存ehcache

4. 注意事项

谨慎使用maxElementsInMemory

maxElementsInMemory表示的是最大缓存多少个key,这个配置项谨慎使用,一般我们应该根据占用多少内存空间来控制,而不是占用多少个key,如果出现某些key的数据量特别大时,就会导致key数量没超过,但内存占用超过导致的OOM了

这个我们通过一个生成大数据量的接口来模拟,其中generateMemoryString方法可以在文末的源码仓库中

1、书写接口

@GetMapping("build")
    @Cacheable(cacheNames = "user", key = "#id")
    public User build(Integer id) {
        System.out.println("get第一次获取,不走缓存");
        User user = new User();
        user.setId(id);
        user.setAge(18);
        // 生成指定大小的字符串
        user.setName(generateMemoryString(id));
        user.setSex(true);
        return user;
    }

2、限制项目JVM内存为100m,方便更快模拟出报错

springboot:缓存不止redis,学会使用本地缓存ehcache

3、调用接口localhost:8080/user/build?id=100,因为该接口会生成大数据,占用本地缓存,而JVM缓存又给的100M,所以调用会报错堆内存溢出,如图所示

springboot:缓存不止redis,学会使用本地缓存ehcache

4、因此该配置项要谨慎使用,可以通过maxBytesLocalHeap,maxBytesLocalDisk设置占用多少内存、磁盘来替代

<cache
            name="user"
            eternal="false"
            maxBytesLocalHeap="50M"
            maxBytesLocalDisk="200M"
            overflowToDisk="false"
            diskPersistent="false"
            timeToLiveSeconds="3600"
            timeToIdleSeconds="0"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
    />

如果maxBytesLocalHeapmaxElementsInMemory都配置了的,谁先达到配置的值,就触发

如果单个key值太大,仍然会导致OOM

虽然我们上面配置了maxBytesLocalHeap来限制最大使用的内存,比如我们限制了该值为100M,则如果我们有4个30M的数据进来,那么就会根据配置的淘汰策略去淘汰之前的key,以腾出空间来装新的数据

但如果新进来的数据很大,比如超过100M了,那么就会一下子装满内存,甚至淘汰之前的key也不行,所以这种情况下还是会导致OOM的

遇到这种情况,两种处理办法,一种是保证不会有大于这个阈值的数据产生,这个可以通过业务代码控制,二是设置一个全局错误捕捉,捕捉产生的OOM报错,然后返回一个兜底或者其他的状态码,以此标识

演示源码

https://gitee.com/wuhanxue/wu_study/tree/master/demo/local_cache_demo文章来源地址https://www.toymoban.com/news/detail-429608.html

到了这里,关于springboot:缓存不止redis,学会使用本地缓存ehcache的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot使用Redis作为缓存器缓存数据的操作步骤以及避坑方案

    2.1使用之前要明确使用的业务场景 例如我们在登录时,可以让redis缓存验证码,又如在分类下显示菜品数据时,我们可以对分类和菜品进行缓存数据等等。 2.2导入Redis相关依赖 2.3在使用的controller层导入RedisTemplate 例如: 说明一下:这里为什么使用@Resource注解而不使用@Autowi

    2024年02月16日
    浏览(51)
  • 高并发下缓存失效问题(穿透、雪崩、击穿),以及本地锁、Redis分布锁、Redisson锁、SpringCache使用

    说明 :以不存在的数据攻击,数据库压力增加导致崩溃 风险 :利用不存在数据攻击,数据库瞬时压力增大,导致崩溃 解决 :设置不存在数据为 null 值 与 短暂过期时间 布隆过滤器 使用案例: redission布隆过滤器解决缓存穿透问题,定时刷新bloomFilter中的数据 说明 :设置缓

    2024年02月08日
    浏览(54)
  • 多级缓存(nginx本地缓存、JVM进程缓存、redis缓存)

    Caffeine示例 封装完函数之后,我们对nginx.conf进行修改(请求进来之后会去寻找item.lua) item.lua文件内容 上面的item.lua文件中需要进行拼接数据,我们需要JSON结果处理 在实际生产中tomcat是肯定以集群的方式存在 当我们修改nginx.conf发送请求为集群的时候,如下图 这个时候存在

    2024年01月17日
    浏览(55)
  • 23-MyBatis缓存、本地缓存、分布式Redis缓存、前端缓存

             MyBatis一级缓存、          MyBatis二级缓存、          本地缓存:单节点          分布式Redis缓存:多节点          前端sessionStorage缓存:会话缓存          前端localStorage缓存:前端本地缓存 MyBatis一级缓存默认是开启的。 在Spring Boot中需要添加

    2024年02月13日
    浏览(36)
  • 缓存的变更(JVM本地缓存->Redis分布式缓存)

    在一次需求修改中,下游的服务附加提出了,针对某个业务数据缓存的生效时间的要求 原JVM设计方案: 采用jvm本地缓存机制,定时任务30秒刷新一次 现在redis方案: 因为很多地方使用了这个业务数据缓存,使用方面不能改动过多 因为是分布式部署,如果只使用jvm缓存,无法

    2024年02月11日
    浏览(48)
  • springboot+redis+mysql+quartz-使用pipeline+lua技术将缓存数据定时更新到数据库

    代码讲解:7.3点赞功能-定时持久化到数据库-Java程序整合pipeline+lua_哔哩哔哩_bilibili https://www.bilibili.com/video/BV1Lg4y1w7U9 代码: blogLike_schedule/like08 · xin麒/XinQiUtilsOrDemo - 码云 - 开源中国 (gitee.com) https://gitee.com/flowers-bloom-is-the-sea/XinQiUtilsOrDemo/tree/master/blogLike_schedule/like08 数据库表:

    2024年02月13日
    浏览(43)
  • springboot+redis+mysql+quartz-通过Java操作jedis使用pipeline获取缓存数据定时更新数据库

    代码讲解:6-点赞功能-定时持久化到数据库-pipeline+lua-优化pipeline_哔哩哔哩_bilibili https://www.bilibili.com/video/BV1yP411C7dr 代码: blogLike_schedule/like06 · xin麒/XinQiUtilsOrDemo - 码云 - 开源中国 (gitee.com) https://gitee.com/flowers-bloom-is-the-sea/XinQiUtilsOrDemo/tree/master/blogLike_schedule/like06 数据库表的

    2024年02月16日
    浏览(48)
  • springboot+redis+mysql+quartz-通过Java操作jedis定时使用lua脚本获取缓存数据并更新数据库

    springboot+redis+mysql+quartz-通过Java操作jedis定时使用lua脚本获取缓存数据并更新数据库 代码讲解:7.1点赞功能-定时持久化到数据库-Java整合lua_哔哩哔哩_bilibili https://www.bilibili.com/video/BV1ZX4y1H7JT/ 代码: blogLike_schedule/like07 · xin麒/XinQiUtilsOrDemo - 码云 - 开源中国 (gitee.com) https://gitee

    2024年02月13日
    浏览(55)
  • 【业务功能篇87】微服务-springcloud-本地缓存-redis-分布式缓存-缓存穿透-雪崩-击穿

      缓存的作用是减低对数据源的访问频率。从而提高我们系统的性能。 缓存的流程图 2.1 本地缓存   其实就是把缓存数据存储在内存中(Map String,Object ).在单体架构中肯定没有问题。 单体架构下的缓存处理 2.2 分布式缓存   在分布式环境下,我们原来的本地缓存就不是

    2024年02月10日
    浏览(56)
  • springboot的缓存和redis缓存,入门级别教程

    一、springboot(如果没有配置)默认使用的是jvm缓存 1、Spring框架支持向应用程序透明地添加缓存。抽象的核心是将缓存应用于方法,从而根据缓存中可用的信息减少执行次数。缓存逻辑是透明地应用的,对调用者没有任何干扰。只要使用@EnableCaching注释启用了缓存支持,Spri

    2024年02月07日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包