【夯实Redis】如何使用内存缓存与Redis缓存实现多级缓存?

您所在的位置:网站首页 苹果耳机连不上蓝牙怎么办 【夯实Redis】如何使用内存缓存与Redis缓存实现多级缓存?

【夯实Redis】如何使用内存缓存与Redis缓存实现多级缓存?

2024-07-09 23:24:35| 来源: 网络整理| 查看: 265

目录

多级缓存实现方案

参考实例代码

多级缓存实现方案

       首先看一下流程图。客户端在获取数据的时候,首先向当前服务所在内存请求缓存数据。如果内存中有缓存数据则直接返回缓存数据。如果没有内存缓存,则向分布式缓存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--------目录帖



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭