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

news2024/9/22 3:40:01

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

在这里插入图片描述

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

在这里插入图片描述

3、调用更新接口

在这里插入图片描述

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

在这里插入图片描述

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,方便更快模拟出报错

在这里插入图片描述

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

在这里插入图片描述

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报错,然后返回一个兜底或者其他的状态码,以此标识

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/447369.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python+vue 市政工程资源互助平台的设计与实现

该系统将由用户系统&#xff0c;管理员系统两部分组成。用户有个人和vip两种类型。 用户模块包括登录注册功能&#xff0c;登录字段包括用户名&#xff0c;密码&#xff0c;用户身份以及验证码。注册包括用户名&#xff0c;密码&#xff0c;邮箱&#xff0c;电话号码&#xff0…

vite+vue+element-plus完成一个admin管理后台

整体项目的 访问链接&#xff1a;https://bigmiss.top/demo/index.html 用到的技术整理 名称版本安装命令说明vite4.0.0npm init vitelatest构建Vue项目vue3.2.45npm install vuenext渐进式框架&#xff08;在vite已安装&#xff09;element-plus3.7.5npm install element-plu…

C#调用C++封装的SDK库(dll动态库)——下

C#调用C封装的SDK库(dll动态库)——下 一、说明 上一篇我们相当于封装的是C语言风格的动态dll库&#xff0c;供C#来调用的。 C#调用C封装的SDK库(dll动态库)——上 如果我们要封装的是下面的类呢&#xff1f;我们该怎么办&#xff1f;大家先思考下。 class Calculation { p…

Tomcat常用操作

Tomcat时间长不用&#xff0c;居然不会用了&#xff0c;这里用的Tomcat9.0.74&#xff0c;对应的jdk版本是jdk8与jdk11。 先看他的把Tomcat安装上去&#xff1a;Tomcat9的安装 运行与关闭Tomcat winr输入cmd。在运行窗口输入startup.bat&#xff0c;回车&#xff0c;启动Tom…

Java基础:对象的克隆(复制)

假如想复制一个简单变量。很简单&#xff1a; int apples 5; int pears apples; 不仅int类型&#xff0c;其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。 但是如果你复制的是一个对象&#xff0c;情况就复杂了。 假设说我是一个b…

webpack----开发服务器

文章目录 devServer抽取csscss的兼容性压缩cssjs语法检查js的兼容性 devServer 每次编辑源码后&#xff0c;都要webpack重新打包&#xff0c;才能看到效果&#xff0c;麻烦&#xff01;使用webpack-dev-server 自动打包编译源码配置 // webpack.config.js ... mode: "de…

shell中的for循环和if判断

一.编写脚本for1.sh,使用for循环创建20账户&#xff0c;账户名前缀由用户从键盘输入&#xff0c;账户初始密码由用户输入&#xff0c;例如: test1、test2、test3、.....、 test10 1.创建脚本for1.sh [rootserver ~]# vim for1.sh 2.编写脚本for1.sh 3.执行脚本for1.sh [roo…

linux命令----- mkdir与rmdir

创建与删除目录 一 mkdir1.mkdir 目录名2.mkdir -p 目录一/目录二 二 rmdir1.rmdir 目录名2.删除非空目录时失败3. rmkdir -p 目录1/目录2 一 mkdir mkdir是make directories的缩写&#xff0c;主要用于linux中创建目录 创建的目录不能和同级目录中已经存在的目录重名可以mkd…

【产品经理】系统上线自查清单

产品上线之前的准备工作&#xff0c;看起来简单&#xff0c;实际做起来是非常繁杂的&#xff0c;如果没有尽早考虑和准备&#xff0c;可能会手忙脚乱甚至导致产品延迟上线。 产品上线前的准备工作听起来简单&#xff0c;但实际做起来非常繁杂。除了要考虑用户需求、商业需求外&…

计算广告(十八)

营销组合模型 MMM 分析背景 随着媒体类型和销售渠道的不断变化&#xff0c;客户旅程日益复杂化。单一活动层面的优化已无法满足客户需求。为了应对这一挑战&#xff0c;品牌方需在战略和活动层面构建完整的营销视图&#xff0c;优化各营销渠道间的效率并实现最高投资回报率。…

【BIM+GIS】ArcGIS Pro3.0打开多种格式三维模型案例教程

本文讲解在ArcGIS Pro3.0打开BIM模型(.rvt)、倾斜模型OSGB、Sketchup(.skp)、3d max(.3ds)、点云数据(.las)的方法及注意事项。 文章目录 一、ArcGIS Pro打开BIM(.rvt)二、ArcGIS Pro打开倾斜OSGB三、ArcGIS Pro打开Sketchup(.skp)四、ArcGIS Pro打开3d max(.3ds)…

C++ [模板]

本文已收录至《C语言》专栏&#xff01; 作者&#xff1a;ARMCSKGT 目录 前言 正文 泛型编程 问题引入 泛型 函数模板 概念 格式 使用方式 模板原理 模板的实例化 隐式实例化 显示实例化 模板匹配规则 类模板 类模板定义格式 类模板的实例化 非类型模板参数 …

【13 Listener 学习笔记】

Listener 笔记记录 1. Listener监听器2. 监听对象的监听器2.1 ServletContetListener2.2 HttpSessionListener2.3 ServletRequestListener 3. 监听域对象属性变化的监听器3.1 ServletContextAttributeListener3.2 HttpSessionAttributeListener3.3 ServletRequestAttributeList…

每日一个小技巧:如何去水印而不损图片?赶紧学起来

在数码时代中&#xff0c;照片的处理与分享已经成为了我们日常中不可或缺的一部分。但是&#xff0c;大家在网上保存的图片常常会带有水印&#xff0c;非常影响图片的观赏性。水印旨在防止照片被盗用或侵权&#xff0c;但有时候它也很破坏照片的美感&#xff0c;因此许多人都在…

超级实用的C++学习网站

重要说明&#xff1a;该博客长期更新&#xff0c;方便读者查阅&#xff01; 一、参考资料 学习C这几个网站足矣 二、C学习网站 C中文网 cppreference 当之无愧的C学习第一网站。该网站希望给程序员提供一个关于C和C的完整的在线参考&#xff0c;所以它的内容非常的丰富。有…

动态类型语言、静态类型语言、强类型语言、弱类型语言解释

首先要明确这些名词都是针对数据类型展开的各自定义&#xff0c;同样针对数据类型在编译时和运行时会有一些限定或者规则存在。动态类型语言不能等同于弱类型语言&#xff0c;静态类型语言也不能等同于强类型语言。 静态类型语言和动态类型语言放到一个维度来进行评价类型系统&…

ClickHouse物化视图

目录 1 概述1.1 物化视图与普通视图的区别1.2 优缺点1.3 基本语法 2 案例实操2.1 准备测试用表和数据2.2 创建物化视图2.3 导入增量数据2.4 导入历史数据 1 概述 ClickHouse 的物化视图是一种查询结果的持久化&#xff0c;它确实是给我们带来了查询效率的提升。用户查起来跟表没…

MYSQL---主从同步概述与配置

一、MYSQL主从同步概述 1、什么是MySQL主从同步&#xff1f; 实现数据自动同步的服务结构 主服务器(master): 接受客户端访问连接 从服务器(slave)&#xff1a;自动同步主服务器数据 2、主从同步原理 Maste&#xff1a;启用binlog 日志 Slave&#xff1a;Slave_IO: 复制master主…

CPU寄存器的分类与Intel 8086 的eax,ebx,ecx,edx

目录 一、CPU中的寄存器分类 1.用户可见寄存器 2.控制和状态寄存器 一、CPU中的寄存器分类 大致分为两类: 一类属于用户可见寄存器&#xff0c;对这类寄存器编程&#xff0c;以及通过优化使CPU因使用这类寄存器&#xff0c;而减少对主存的访问次数&#xff0c; 另一类属于控…

MPRC086444-005对其进行维护和管理,以确保系统的稳定性和可靠性。

​ MPRC086444-005对其进行维护和管理&#xff0c;以确保系统的稳定性和可靠性。 变电站自动化系统优缺点 变电站自动化系统结构 变电站自动化系统优缺点 变电站自动化系统是以计算机技术、自动控制技术及通信技术为核心&#xff0c;对变电站及配电系统各个环节进行自动化控制和…