Spring Boot2.0+Redis+Ehcache实现二级缓存
EHCache 本地缓存
Redis 分布式缓存(可以共享)
一级 Redis 二级Ehcache 当redis挂了 有备胎
反之:
先走本地,本地没有再走网络 尽量少走Redis 效率会高一些
Redis与数据库的区别:
相同点 都是需要进行网络连接
不同点 是存放的介质 内存 和 硬盘
数据库需要做IO操作 性能比直接操作内存效率要低
Ehchache
不需要走网络 直接从内存中获取
由于Ehchache容器限制,会持久化在硬盘上,
Redis+ehCache实现两级级缓存
spring boot中集成了spring cache,并有多种缓存方式的实现,如:Redis、Caffeine、JCache、EhCache等等。但如果只用一种缓存,要么会有较大的网络消耗(如Redis),要么就是内存占用太大(如Caffeine这种应用内存缓存)。在很多场景下,可以结合起来实现一、二级缓存的方式,能够很大程度提高应用的处理效率。
内容说明:
缓存、两级缓存
spring cache:主要包含spring cache定义的接口方法说明和注解中的属性说明
spring boot + spring cache:RedisCache实现中的缺陷
caffeine简介
spring boot + spring cache 实现两级缓存(redis + caffeine)
缓存、两级缓存
简单的理解,缓存就是将数据从读取较慢的介质上读取出来放到读取较快的介质上,如磁盘-->内存。平时我们会将数据存储到磁盘上,如:数据库。如果每次都从数据库里去读取,会因为磁盘本身的IO影响读取速度,所以就有了像redis这种的内存缓存。可以将数据读取出来放到内存里,这样当需要获取数据时,就能够直接从内存中拿到数据返回,能够很大程度的提高速度。但是一般redis是单独部署成集群,所以会有网络IO上的消耗,虽然与redis集群的链接已经有连接池这种工具,但是数据传输上也还是会有一定消耗。所以就有了应用内缓存,如:caffeine。当应用内缓存有符合条件的数据时,就可以直接使用,而不用通过网络到redis中去获取,这样就形成了两级缓存。应用内缓存叫做一级缓存,远程缓存(如redis)叫做二级缓存
过期问题:
一级缓存 过期 时间 比二级要短一些
目录结构:
实体类:
注意一定要 序列号
package com.toov5.entity; import java.io.Serializable; import lombok.Data; @Data public class Users implements Serializable{ private String name; private Integer age; }
controller层
package com.toov5.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.toov5.entity.Users; import com.toov5.service.UserService; @RestController public class IndexController { @Autowired private UserService userService; @RequestMapping("/userId") public Users getUserId(Long id){ return userService.getUser(id); } }
service层
package com.toov5.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.ehcache.EhCacheCacheManager; import org.springframework.stereotype.Component; import net.sf.ehcache.Cache; import net.sf.ehcache.Element; @Component public class EhCacheUtils { // @Autowired // private CacheManager cacheManager; @Autowired private EhCacheCacheManager ehCacheCacheManager; // 添加本地缓存 (相同的key 会直接覆盖) public void put(String cacheName, String key, Object value) { Cache cache = ehCacheCacheManager.getCacheManager().getCache(cacheName); Element element = new Element(key, value); cache.put(element); } // 获取本地缓存 public Object get(String cacheName, String key) { Cache cache = ehCacheCacheManager.getCacheManager().getCache(cacheName); Element element = cache.get(key); return element == null ? null : element.getObjectValue(); } public void remove(String cacheName, String key) { Cache cache = ehCacheCacheManager.getCacheManager().getCache(cacheName); cache.remove(key); } }
package com.toov5.service; import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; @Component public class RedisService { @Autowired private StringRedisTemplate stringRedisTemplate; //这样该方法支持多种数据类型 public void set(String key , Object object, Long time){ //开启事务权限 stringRedisTemplate.setEnableTransactionSupport(true); try { //开启事务 stringRedisTemplate.multi(); String argString =(String)object; //强转下 stringRedisTemplate.opsForValue().set(key, argString); //成功就提交 stringRedisTemplate.exec(); } catch (Exception e) { //失败了就回滚 stringRedisTemplate.discard(); } if (object instanceof String ) { //判断下是String类型不 String argString =(String)object; //强转下 //存放String类型的 stringRedisTemplate.opsForValue().set(key, argString); } //如果存放Set类型 if (object instanceof Set) { Set<String> valueSet =(Set<String>)object; for(String string:valueSet){ stringRedisTemplate.opsForSet().add(key, string); //此处点击下源码看下 第二个参数可以放好多 } } //设置有效期 if (time != null) { stringRedisTemplate.expire(key, time, TimeUnit.SECONDS); } } //做个封装 public void setString(String key, Object object){ String argString =(String)object; //强转下 //存放String类型的 stringRedisTemplate.opsForValue().set(key, argString); } public void setSet(String key, Object object){ Set<String> valueSet =(Set<String>)object; for(String string:valueSet){ stringRedisTemplate.opsForSet().add(key, string); //此处点击下源码看下 第二个参数可以放好多 } } public String getString(String key){ return stringRedisTemplate.opsForValue().get(key); } }
package com.toov5.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.alibaba.fastjson.JSONObject; import com.toov5.entity.Users; import com.toov5.mapper.UserMapper; import ch.qos.logback.core.net.SyslogOutputStream; import io.netty.util.internal.StringUtil; @Component public class UserService { @Autowired private EhCacheUtils ehCacheUtils; @Autowired private RedisService redisService; @Autowired private UserMapper userMapper; //定义个全局的cache名字 private String cachename ="userCache"; public Users getUser(Long id){ //先查询一级缓存 key以当前的类名+方法名+id+参数值 String key = this.getClass().getName() + "-" + Thread.currentThread().getStackTrace()[1].getMethodName() + "-id:" + id; //查询一级缓存数据有对应值的存在 如果有 返回 Users user = (Users)ehCacheUtils.get(cachename, key); if (user != null) { System.out.println("key"+key+",直接从一级缓存获取数据"+user.toString()); return user; } //一级缓存没有对应的值存在,接着查询二级缓存 // redis存对象的方式 json格式 然后反序列号 String userJson = redisService.getString(key); //如果rdis缓存中有这个对应的值,修改一级缓存 最下面的会有的 相同会覆盖的 if (!StringUtil.isNullOrEmpty(userJson)) { //有 转成json JSONObject jsonObject = new JSONObject();//用的fastjson Users resultUser = jsonObject.parseObject(userJson,Users.class); ehCacheUtils.put(cachename, key, resultUser); return resultUser; } //都没有 查询DB Users user1 = userMapper.getUser(id); if (user1 == null) { return null; } //存放到二级缓存 redis中 redisService.setString(key, new JSONObject().toJSONString(user1)); //存放到一级缓存 Ehchache ehCacheUtils.put(cachename, key, user1); return user1; } }
启动类:
package com.toov5.app; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @EnableCaching //开启缓存 @MapperScan(basePackages={"com.toov5.mapper"}) @SpringBootApplication(scanBasePackages={"com.toov5.*"}) public class app { public static void main(String[] args) { SpringApplication.run(app.class, args); } }
app1.ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <diskStore path="java.io.tmpdir/ehcache-rmi-4000" /> <!-- 默认缓存 --> <defaultCache maxElementsInMemory="1000" eternal="true" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000" diskPersistent="true" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> <!-- demo缓存 --><!-- name="userCache" 对应我们在 @CacheConfig(cacheNames={"userCache"}) !!!!! --> <!--Ehcache底层也是用Map集合实现的 --> <cache name="userCache" maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <!-- LRU缓存策略 --> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" /> <!-- 用于在初始化缓存,以及自动设置 --> <bootstrapCacheLoaderFactory class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" /> </cache> </ehcache>
yml
###端口号配置 server: port: 8080 ###数据库配置 spring: datasource: url: jdbc:mysql://localhost:3306/test username: root password: root driver-class-name: com.mysql.jdbc.Driver test-while-idle: true test-on-borrow: true validation-query: SELECT 1 FROM DUAL time-between-eviction-runs-millis: 300000 min-evictable-idle-time-millis: 1800000 # 缓存配置读取 cache: type: ehcache ehcache: config: classpath:app1_ehcache.xml redis: database: 0 host: 192.168.91.3 port: 6379 password: 123 jedis: pool: max-active: 8 max-wait: -1 max-idle: 8 min-idle: 0 timeout: 10000
运行结果:
后面的访问,在控制台打印:
其中一定要注意
这里的 两级缓存时间问题 执行设置二级缓存时候 需要时间的 所以这两个时间设置问题一定要注意了哦
Spring Boot2.0+Redis+Ehcache实现二级缓存的更多相关文章
- 【Redis】SpringBoot+Redis+Ehcache实现二级缓存
一.概述 1.1 一些疑惑? 1.2 场景 1.3 一级缓存.两级缓存的产生 1.4 流程分析 二.项目搭建 一.概述 1.1 一些疑惑? Ehcache本地内存 Redis 分布式缓存可以共享 一级 ...
- SpringBoot30 整合Mybatis-Plus、整合Redis、利用Ehcache实现二级缓存、利用SpringCache和Redis作为缓存
1 环境说明 JDK: 1.8 MAVEN: 3. SpringBoot: 2.0.4 2 SpringBoot集成Mybatis-Plus 2.1 创建SpringBoot 利用IDEA创建Spri ...
- Hibernate4+EhCache配置二级缓存
本文主要讲一讲Hibernate+EhCache配置二级缓存的基本使用方法 (有关EhCache的基础介绍可参见:http://sjsky.iteye.com/blog/1288257 ) Cache ...
- 【redis】4.spring boot集成redis,实现数据缓存
参考地址:https://spring.io/guides/gs/messaging-redis/ ================================================== ...
- mybatis(4)_二级缓存深入_使用第三方ehcache配置二级缓存
增删改对二级缓存的影响 1.增删改也会清空二级缓存 2.对于二级缓存的清空实质上是对value清空为null,key依然存在,并非将Entry<k,v>删除 3.从DB中进行select查 ...
- Spring Boot2.0 整合 Kafka
Kafka 概述 Apache Kafka 是一个分布式流处理平台,用于构建实时的数据管道和流式的应用.它可以让你发布和订阅流式的记录,可以储存流式的记录,并且有较好的容错性,可以在流式记录产生时就进 ...
- spring boot 2.0(一)权威发布spring boot2.0
Spring Boot2.0.0.RELEASE正式发布,在发布Spring Boot2.0的时候还出现一个小插曲,将Spring Boot2.0同步到Maven仓库的时候出现了错误,然后Spring ...
- Spring Boot2.0 设置拦截器
所有功能完成 配置登录认证 配置拦截器 在spring boot2.0 之后 通过继承这个WebMvcConfigurer类 就可以完成拦截 新建包com.example.interceptor; 创 ...
- Spring Boot2.0 静态资源被拦截问题
在Spring Boot2.0+的版本中,只要用户自定义了拦截器,则静态资源会被拦截.但是在spring1.0+的版本中,是不会拦截静态资源的. 因此,在使用Spring Boot2.0+时,配置拦截 ...
随机推荐
- Jackson转换JSON例子
Jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json.xml转换成Java对象. 前面有介绍过json-lib这个框架,在线博文:http://www.cnblo ...
- 填报志愿(codevs 2930)
题目描述 Description 高考已经结束,而志愿填报正在进行中- 吴校长的学校里有n位同学,每位同学有ki个愿意去的大学.而在吴老师的省份中,有m所大学有招生名额.根据往年的经验,对于每所大学( ...
- 品酒大会 BZOJ 4199
品酒大会 [问题描述] [输入格式] [输出格式] [样例输入] 10ponoiiipoi 2 1 4 7 4 8 3 6 4 7 [样例输出] 45 56 10 56 3 32 0 0 0 0 0 ...
- linux命令netstat或ifconfig未找到
linux命令netstat或ifconfig未找到 linux使用netstat或者ifconfig命令时,显示命令未找到.通过yum search netstat这个命令,匹配结果如下:===== ...
- ajax 分页(jquery分页插件pagination) 小例1
<link rel="stylesheet" href="/plugins/jQuery/page/pagination.css"/> <sc ...
- 重写enum的valueof方法等
enum 对象的常用方法介绍 int compareTo(E o) 比较此枚举与指定对象的顺序. Class<E> getDeclaringClass() ...
- 有关WebView开发问题(转)
http://blog.sina.com.cn/s/blog_8241e8510101btvk.html 如何创建WebView: 1.添加权限:AndroidManifest.xml中必须使用许可& ...
- ****如何优雅的用Axure装逼?高保真原型心得分享
本文核心内容点:- 啥是高保真原型?(附简单说明原型)- Axure可以画出什么水准的高保真?(给示例,开启装逼模式)- 高保真原型图技巧:- 啥时候上高保真?适用场景 and 不适用场景 啥是高保真 ...
- 5.Longest Palindrome substring
/* * 5.Longest Palindrome substring * 2016-4-9 by Mingyang 自然而然的想到用dp来做 * 刚开始自己做的时候分的条件太细,两个index相等, ...
- 深入GCD(五):资源竞争
概述我将分四步来带大家研究研究程序的并发计算.第一步是基本的串行程序,然后使用GCD把它并行计算化.如果你想顺着步骤来尝试这些程序的话,可以下载源码.注意,别运行imagegcd2.m,这是个反面教材 ...