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)叫做二级缓存

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.learn</groupId><artifactId>springboot2.0-redis</artifactId><version>0.0.1-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.0.RELEASE</version></parent><dependencies><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><!-- SpringBoot web 核心组件 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--开启 cache 缓存 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!-- ehcache缓存 --><dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache</artifactId><version>2.9.1</version><!--$NO-MVN-MAN-VER$ --></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.1.1</version></dependency><!-- mysql 依赖 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency></dependencies></project>
spring:datasource:url: jdbc:mysql://localhost:3306/testusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Drivertest-while-idle: truetest-on-borrow: truevalidation-query: SELECT 1 FROM DUALtime-between-eviction-runs-millis: 300000min-evictable-idle-time-millis: 1800000redis:database: 0host: localhostport: 6379password: 123456jedis:pool:max-active: 8max-wait: -1max-idle: 8min-idle: 0timeout: 10000# 缓存配置读取cache:type: ehcacheehcache:config: classpath: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缓存 --><cache name="userCache" maxElementsInMemory="1000" eternal="false"timeToIdleSeconds="120" timeToLiveSeconds="300" overflowToDisk="true"diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000" diskPersistent="false"diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"><cacheEventListenerFactoryclass="net.sf.ehcache.distribution.RMICacheReplicatorFactory" /><!-- 用于在初始化缓存,以及自动设置 --><bootstrapCacheLoaderFactoryclass="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" /></cache>
</ehcache>
package com.learn.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;@Autowiredprivate 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.learn.service;import java.util.Set;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;@Component
public class RedisService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;// public void set(String key, Object object, Long time) {// stringRedisTemplate.opsForValue();// // 存放String 类型// if (object instanceof String) {// setString(key, object);// }// // 存放 set类型// if (object instanceof Set) {// setSet(key, object);// }// // 设置有效期 以秒为单位// stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);// }//public void setString(String key, Object object) {// 开启事务权限stringRedisTemplate.setEnableTransactionSupport(true);try {// 开启事务 beginstringRedisTemplate.multi();String value = (String) object;stringRedisTemplate.opsForValue().set(key, value);System.out.println("存入完毕,马上开始提交redis事务");// 提交事务stringRedisTemplate.exec();} catch (Exception e) {// 需要回滚事务stringRedisTemplate.discard();}}public void setSet(String key, Object object) {Set<String> value = (Set<String>) object;for (String oj : value) {stringRedisTemplate.opsForSet().add(key, oj);}}public String getString(String key) {return stringRedisTemplate.opsForValue().get(key);}}
package com.learn.service;import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import com.alibaba.fastjson.JSONObject;
import com.learn.entity.Users;
import com.learn.mapper.UserMapper;@Service
public class UserService {@Autowiredprivate EhCacheUtils ehCacheUtils;private static final String CACHENAME_USERCACHE = "userCache";@Autowiredprivate RedisService redisService;@Autowiredprivate UserMapper userMapper;public Users getUser(Long id) {String key = this.getClass().getName() + "-" + Thread.currentThread().getStackTrace()[1].getMethodName()+ "-id:" + id;// 1.先查找一级缓存(本地缓存),如果本地缓存有数据直接返回Users ehUser = (Users) ehCacheUtils.get(CACHENAME_USERCACHE, key);if (ehUser != null) {System.out.println("使用key:" + key + ",查询一级缓存 ehCache 获取到ehUser:" + JSONObject.toJSONString(ehUser));return ehUser;}// 2. 如果本地缓存没有该数据,直接查询二级缓存(redis)String redisUserJson = redisService.getString(key);if (!StringUtils.isEmpty(redisUserJson)) {// 将json 转换为对象(如果二级缓存redis中有数据直接返回二级缓存)JSONObject jsonObject = new JSONObject();Users user = jsonObject.parseObject(redisUserJson, Users.class);// 更新一级缓存ehCacheUtils.put(CACHENAME_USERCACHE, key, user);System.out.println("使用key:" + key + ",查询二级缓存 redis 获取到ehUser:" + JSONObject.toJSONString(user));return user;}// 3. 如果二级缓存redis中也没有数据,查询数据库Users user = userMapper.getUser(id);if (user == null) {return null;}// 更新一级缓存和二级缓存String userJson = JSONObject.toJSONString(user);redisService.setString(key, userJson);ehCacheUtils.put(CACHENAME_USERCACHE, key, user);System.out.println("使用key:" + key + ",一级缓存和二级都没有数据,直接查询db" + userJson);return user;}}
package com.learn.api.controller;import java.util.HashSet;
import java.util.Set;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import com.learn.entity.Users;
import com.learn.service.RedisService;
import com.learn.service.UserService;@RestController
public class IndexControler {@Autowiredprivate RedisService redisService;@Autowiredprivate UserService userService;@RequestMapping("/setString")public String setString(String key, String value) {// redisService.set(key, value, 60l);redisService.setString(key, value);return "success";}@RequestMapping("/getString")public String getString(String key) {return redisService.getString(key);}@RequestMapping("/setSet")public String setSet() {Set<String> set = new HashSet<String>();set.add("yushengjun");set.add("lisi");redisService.setSet("setTest", set);return "success";}@RequestMapping("/getUser")public Users getUser(Long id) {Users user = userService.getUser(id);return user;}
}
package com;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;@SpringBootApplication
@MapperScan("com.learn.mapper")
@EnableCaching
public class AppRedis {public static void main(String[] args) {SpringApplication.run(AppRedis.class, args);}}

项目整合一级缓存和二级缓存相关推荐

  1. Mybatis | Mybatis 一级缓存、二级缓存、三级自定义缓存(Redis)

    Mybatis 一级缓存.二级缓存.三级自定义缓存Redis实现 一.Mybatis 缓存 二.一级缓存 SqlSession级别 1.如何开启一级缓存 2.什么时候清除缓存? 3.什么时候缓存失效? ...

  2. Mybatis一级缓存,二级缓存的实现就是这么简单

    介绍 又到了一年面试季,所以打算写一点面试常问的东西,争取说的通俗易懂.面试高级岗,如果你说熟悉Mybatis,下面这些问题基本上都会问 Mybatis插件的实现原理? 如何写一个分页插件? Myba ...

  3. MyBatis】MyBatis一级缓存和二级缓存

    转载自  MyBatis]MyBatis一级缓存和二级缓存 MyBatis自带的缓存有一级缓存和二级缓存 一级缓存 Mybatis的一级缓存是指Session缓存.一级缓存的作用域默认是一个SqlSe ...

  4. Mybatis一级缓存和二级缓存 Redis缓存

    一级缓存 Mybatis的一级缓存存放在SqlSession的生命周期,在同一个SqlSession中查询时,Mybatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对 ...

  5. MyBatis中的一级缓存和二级缓存介绍

    先说缓存,合理使用缓存是优化中最常见的,将从数据库中查询出来的数据放入缓存中,下次使用时不必从数据库查询,而是直接从缓存中读取,避免频繁操作数据库,减轻数据库的压力,同时提高系统性能. 一级缓存 一级 ...

  6. mybatis 详解------ 一级缓存、二级缓存(九)

    mybatis 为我们提供了一级缓存和二级缓存,可以通过下图来理解: ①.一级缓存是SqlSession级别的缓存.在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMa ...

  7. java mysql 二级缓存_深入理解MyBatis中的一级缓存与二级缓存

    http://blog.csdn.net/weixin_36380516/article/details/73194758 先说缓存,合理使用缓存是优化中最常见的,将从数据库中查询出来的数据放入缓存中 ...

  8. Mybatis缓存机制(一级缓存、二级缓存、三级缓存)

    一.含义: 缓存就是内存中的数据,常常来自对数据库查询结果的保存. 使用缓存,我们可以避免频繁与数据库进行交互,从而提高响应速度. Mybatis的缓存分为一级缓存.二级缓存.三级缓存. 一级缓存: ...

  9. SpringBoot整合Redis配置MyBatis二级缓存

    目录 写在前面 源码获取 一.MyBatis缓存机制 1.1.一级缓存 1.2.二级缓存 二.集成Redis 2.1.安装Redis 2.2.项目引入Redis 2.2.1.Maven依赖 2.2.2 ...

最新文章

  1. 关于ios中编译ffmpeg0.9.2库
  2. Android移动开发之【Android实战项目】在Service中弹出Dialog对话框,即全局性对话框
  3. 汇编语言 乘法和除法指令
  4. 您应该保持联系的十大高级Java对话
  5. linux 找不到php命令,bash scp:未找到命令的解决方法
  6. 数据本地存储方法封装(笔记)localStorage、sessionStorage
  7. Android 系统(151)----添加开关机铃声
  8. 微软将发布新版Win10 可能以“新 Windows”之名公布
  9. 046、JVM实战总结:动手实验:自己动手模拟出对象进入老年代的场景体验一下(下)
  10. yum install php72w,yum 安装 php72w-pecl-imagick 报错
  11. Android studio快捷键【Mac、Win\Linux】
  12. 求书:推荐阅读倡议书
  13. 查询子串_你知道什么是 MySQL 的模糊查询?
  14. python第二版课后答案第七章7.5_IDA7.5 启动基础配置
  15. 水平线标记的用法和属性
  16. java,内存,存储
  17. K210——声源定位、声音识别
  18. [CORS:跨域资源共享] 同源策略与JSONP
  19. MQL4自编指标学习1
  20. 我在ADAU1452这颗音频DSP上,用MFXLMS算法来做Active Noise Cancellation(ANC)

热门文章

  1. JavaScript_process01
  2. Xml+Xslt测试工具
  3. BZOJ4401:块的计数(乱搞)
  4. 小程序渲染html的两种方法
  5. [翻译]应用程序池和应用程序域的区别
  6. Zabbix server is not running 报错的排查
  7. quick-cocos2d-x api构建文档
  8. jackson json 转换Bean, Bean 里没有对应的值 jackson Unrecognized field
  9. Node.js 入门到干活,10 个优质项目就够了!
  10. python文件处理seek_python文件操作 seek(),tell()