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+时,配置拦截 ...
随机推荐
- mysql语句优化方案(网上流传)
关于mysql处理百万级以上的数据时如何提高其查询速度的方法 最近一段时间由于工作需要,开始关注针对Mysql数据库的select查询语句的相关优化方法. 由于在参与的实际项目中发现当mysql表的数 ...
- css样式---隐藏元素
1.通过设置width:0;或height:0 2.将元素的opacity设置成0 3.通过定位将元素移出屏幕范围 4.通过text-indent实现隐藏文字的效果 5.通过z-index隐藏一个元素 ...
- ZOJ 3306 状压dp
转自:http://blog.csdn.net/a497406594/article/details/38442893 Kill the Monsters Time Limit: 7 Seconds ...
- intellij idea 和 myeclipse 转换
原文出处:http://chinaxxren.iteye.com/blog/893970 当只用 intellij idea 建立 工程 1.首先是new project--->create p ...
- Android开发之(1)AnimationListener
1,就像Button控件有监听器一样,动画效果也有监听器,只需要实现AnimationListener就可以实现对动画效果的监听,只需要实现AnimationListener就可以实现对动画效果的监听 ...
- JDK内置工具jstack(Java Stack Trace)(转)
1.介绍 jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的js ...
- dropwatch 网络协议栈丢包检查利器 与 火丁笔记
http://blog.yufeng.info/archives/2497 源码:http://git.fedorahosted.org/cgit/dropwatch.git http://blog. ...
- oracle存储过程中使用字符串拼接
1.使用拼接符号“||” v_sql := 'SELECT * FROM UserInfo WHERE ISDELETED = 0 AND ACCOUNT =''' || vAccount || '' ...
- 用 jQuery实现图片等比例缩放大小
原文:http://www.open-open.com/code/view/1420975773093 <script type="text/javascript"> ...
- 线性表的顺序存储和链式存储的实现(C)
//线性表的顺序存储 #include <stdio.h>typedef int DataType;#define MaxSize 15//定义顺序表typedef struct { Da ...