Redis缓存预热,该如何实现?

花小染 | 500 | 2024-03-25

什么是缓存预热?

缓存预热是一种在程序启动或缓存失效之后,主动将热点数据加载到缓存中的策略。 这样,在实际请求到达程序时,热点数据已经存在于缓存中,从而减少了缓存穿透和缓存击穿的情况,也缓解了SQL服务器的压力。

实现

缓存抽象类

首先我们先来实现一个缓存抽象类,这个抽象类的作用就是在将来我们需要将某个模块的数据需要提前加载到缓存中的时候,我们可以创建一个它的实现类,来进行数据的缓存与加载,具体使用方式请看后边我写的例子。

public abstract class AbstractCache {     /**      * 缓存      */     protected abstract void init();     /**      * 获取缓存      *      * @param <T>      * @return      */     public abstract <T> get();     /**      * 清理缓存      */     public abstract void clear();     /**      * 重新加载      */     public void reload() {         clear();         init();     } }

Spring上下文工具类

接下来我们实现一个Spring的上下文工具类,这个工具类需要实现 ApplicationContextAware,作用就是负责管理bean的加载与实例化的,具体如何使用,请往下继续阅读。

@Component public class ApplicationContextUtil implements ApplicationContextAware {     private static ApplicationContext applicationContext;     @Override     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {         ApplicationContextUtil.applicationContext = applicationContext;     }     /**      * 获取上下文      * @return      */     public static ApplicationContext getContext() {         return applicationContext;     } }

缓存预热

然后我们来实现,在程序启动后,直接进行数据的缓存加载,这个类需要实现 CommandLineRunner接口,这个接口提供的方法作用就是在程序启动后自动运行。

这个实现类里,我们使用 ApplicationContextUtil工具类来获取上下文,然后通过 getBeansOfType方法获取实现 AbstractCache抽象类的子类,返回的是一个Map类型的集合,接下来通过getBean方法以多态的方式实例化子类,最后我们调用抽象类的init方法即可。

如果有多个实现类,使用@Order注解标注先后运行就可以了。

@Component @ConditionalOnProperty(name = {"cache.init.enable"}, havingValue = "true", matchIfMissing = false) public class CachePreheatHandler implements CommandLineRunner {     /**      * 缓存预热      * @param args      * @throws Exception      */     @Override     public void run(String... args) throws Exception {         ApplicationContext context = ApplicationContextUtil.getContext();         Map<String, AbstractCache> beansOfType = context.getBeansOfType(AbstractCache.class);         for (Map.Entry<String, AbstractCache> cacheEntry : beansOfType.entrySet()) {             AbstractCache cache = context.getBean(cacheEntry.getValue().getClass());             cache.init();         }     } }

解释:

@ConditionalOnProperty这个注解在这里的作用是,需要在配置文件开启 cache.init.enable,理想值是true,默认值是false。

cache.init.enable=true

使用

我们就以新闻热点为例,数据库中有一张tb_news新闻表,均为微博热搜体育榜内容。

bde300aa9e4947eb8df696a4933c0e58~tplv-k3u1fbpfcp-jj-mark 3024 0 0 0 q75.webp

接下来创建一个 AbstractCache的实现类,来实现具体的实现

@Component @RequiredArgsConstructor public class NewsCache extends AbstractCache {     private static final String NEWS_KEY = "news";     private final RedisTemplate<String, Object> redisTemplate;     private final NewsService newsService;     @Override     protected void init() {         if (Boolean.FALSE.equals(redisTemplate.hasKey(NEWS_KEY))) {             redisTemplate.opsForValue().set(NEWS_KEY, newsService.list(), 30, TimeUnit.MINUTES);         }     }     @Override     public <T> T get() {         if (Boolean.FALSE.equals(redisTemplate.hasKey(NEWS_KEY))) {             reload();         }         return (T) redisTemplate.opsForValue().get(NEWS_KEY);     }     @Override     public void clear() {         redisTemplate.delete(NEWS_KEY);     } }

然后启动项目,我们就发现,Redis中已经存好了热点数据

df96794cc0274253ab9c095343663f07~tplv-k3u1fbpfcp-jj-mark 3024 0 0 0 q75.webp

最后可以通过get方法获取数据了,也不用担心数据过期了。

@RestController @RequestMapping("/news") @RequiredArgsConstructor public class NewsController {     private final NewsCache newsCache;     @GetMapping("/cache")     public List<News> list() {         return newsCache.get();     } }

好了,小伙伴们,今天的分享就到此结束了,欢迎留出建议,如果觉得内容可以,还请来个点赞和关注吧!

文章标签: Redis后端相关
推荐指数:

转载声明:

原作者: 寒江雪369

转载自: 《Redis缓存预热如何实现?》,如有侵权,请联系本站删除。

真诚点赞,手留余香

Redis缓存预热,该如何实现?

关于作者 📝

花小染

这个人很懒~

等级 LV4

粉丝 6

获赞 15

经验 667