【夯实Redis】如何使用内存缓存与Redis缓存实现多级缓存? |
您所在的位置:网站首页 › 苹果耳机连不上蓝牙怎么办 › 【夯实Redis】如何使用内存缓存与Redis缓存实现多级缓存? |
目录 多级缓存实现方案 参考实例代码 多级缓存实现方案首先看一下流程图。客户端在获取数据的时候,首先向当前服务所在内存请求缓存数据。如果内存中有缓存数据则直接返回缓存数据。如果没有内存缓存,则向分布式缓存Redis服务器请求数据。如果Redis中存在缓存,则将Redis中的缓存写入内存缓存,并向客户端返回缓存数据。如果Redis中也没有数据,那么只能查询数据库了。查询完数据,需要把此次查询结果分别写入Redis中 与 内存中。 整体流程图如下,实现Java内存缓存与Redis缓存共存的多级缓存,可以提高系统的整体性能与并发能力。缺点是接入Redis缓存后,系统整体的可用性降低,复杂度升高。 首选创建缓存对象。 import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; import lombok.experimental.Accessors; @Data @NoArgsConstructor @AllArgsConstructor @ToString @Accessors(chain = true) public class CacheObject { private T data; private boolean expired; public static CacheObject of(T data) { //测试使用:默认数据是过期的,模拟过期的数据被清除 return new CacheObject().setData(data).setExpired(true); } }接下来创建一个MultiCache对象,并将其加入到Spring容器中。在该Bean中注入RedisTemplate,用于查询Redis。使用@PostConstruct在创建该Bean的时候为该Bean初始化一个守护线程,用于定时清理过期的缓存对象。这里的缓存对象使用了软引用包裹,它会在系统内存不足的时候强制回收掉对象所占内存。 import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.lang.ref.SoftReference; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @Component @RequiredArgsConstructor public class MultiCache { private final ConcurrentHashMap cacheMap = new ConcurrentHashMap(); private static final Logger LOGGER = LoggerFactory.getLogger(MultiCache.class); //每30秒清理一次内存缓存 private static final int CLEAN_INTERVAL = 30; private final RedisTemplate redisTemplate; @Getter @Setter private T dbData; @PostConstruct private void initCleanThread() { Thread cleanThread = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { try { Thread.sleep(CLEAN_INTERVAL * 1000); LOGGER.debug("扫描缓存中过期得垃圾数据...."); //JDK8中Map通过EntrySet根据条件进行过滤,清除已过期的key cacheMap.entrySet().removeIf(entry -> Optional.ofNullable(entry.getValue()) .map(SoftReference::get) .map(CacheObject::isExpired) //没有value值的对象视为过期(有缓存穿透风险,建议配置为false) .orElse(true)); } catch (InterruptedException e) { //提示While循环当前线程已经被打断,可退出循环 Thread.currentThread().interrupt(); } } }); //设置为守护线程 cleanThread.setDaemon(true); cleanThread.start(); }接下来就是调用Get方法获取缓存了。 public T get(String key) { T memoryCache = getMemoryCache(key); if (memoryCache != null) { LOGGER.warn("从内存中获取值:{}", memoryCache); return memoryCache; } T redisCache = getRedisCache(key); if (redisCache != null) { LOGGER.warn("从Redis中获取值:{}", redisCache); return redisCache; } T dataFromDb = getDataFromDb(key); LOGGER.warn("从数据库中获取值:{}", dataFromDb); return dataFromDb; }首先获取的是内存缓存。如果有内存缓存的话,就直接返回。没有的话就继续往下面走。 private T getMemoryCache(String key) { if (cacheMap.containsKey(key)) { SoftReference cacheFromMemory = cacheMap.get(key); if (cacheFromMemory != null && cacheFromMemory.get() != null) { return cacheFromMemory.get().getData(); } } return null; }没有内存缓存则向Redis请求数据,并将分布式缓存写入内存缓存中。 private T getRedisCache(String key) { T redisCache = redisTemplate.opsForValue().get(key); if (redisCache != null) { this.putIntoMemory(key, redisCache); } return redisCache; }如果分布式Redis缓存也没有数据,那么只能向数据库请求数据,并同时将DB查询结果写入内存缓存中与Redis缓存中。 private T getDataFromDb(String key) { T dataFromDb = getDbData(); if (dataFromDb != null) { this.putIntoMemory(key, dataFromDb); this.putIntoRedis(key, dataFromDb); } return dataFromDb; }剩余私有代码 private void putIntoMemory(String key, T value) { CacheObject cacheObject = CacheObject.of(value); cacheMap.put(key, new SoftReference(cacheObject)); } private void putIntoRedis(String key, T value) { redisTemplate.opsForValue().set(key, value); } private void set(String key, T data) { this.putIntoMemory(key, data); this.putIntoRedis(key, data); }最后的Controller测试用例 @RestController @RequestMapping(value = "/pc/api/v1/cache") public class UserCacheController .... @Autowired private MultiCache userCache; @GetMapping(value = "/getUser") public UserDO getUser(String id) { LOGGER.info("paramType is {}", id); //模拟数据库中查询到的数据 userCache.setDbData(new UserDO().setUsername("数据库")); return userCache.get(id); } 阅读更多跟着大宇学Redis--------目录帖 |
今日新闻 |
点击排行 |
|
推荐新闻 |
|
图片新闻 |
|
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭 |