项目整合一级缓存和二级缓存
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);}}?
?
?
總結
以上是生活随笔為你收集整理的项目整合一级缓存和二级缓存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 携程Apollo动态配置日志级别
- 下一篇: 防盗链技术底层实现原理分析