Redis缓存预热,该如何实现

一、什么是缓存预热

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

二、实现

1 、缓存抽象类

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

public abstract class AbstractCache {

    /**
     * 缓存
     */
    protected abstract void init();

    /**
     * 获取缓存
     *
     * @param <T>
     * @return
     */
    public abstract <T> T get();

    /**
     * 清理缓存
     */
    public abstract void clear();

    /**
     * 重新加载
     */
    public void reload() {
        clear();
        init();
    }
}

2、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;
    }
}

3、缓存预热

然后我们来实现,在程序启动后,直接进行数据的缓存加载,这个类需要实现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

4、使用

我们就以新闻热点为例,数据库中有一张tb_news新闻表,均为微博热搜体育榜内容。
在这里插入图片描述

接下来创建一个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中已经存好了热点数据
在这里插入图片描述
最后可以通过get方法获取数据了,也不用担心数据过期了。

@RestController
@RequestMapping("/news")
@RequiredArgsConstructor
public class NewsController {

    private final NewsCache newsCache;

    @GetMapping("/cache")
    public List<News> list() {
        return newsCache.get();
    }
}
### Redis 缓存预热概述 Redis缓存预热是指在应用启动或者特定时间点,预先将可能被频繁访问的数据加载到Redis缓存中[^1]。这一过程不仅涉及简单的数据加载操作,还包含了对缓存数据的维护机制,以确保缓存中的数据能够及时更新并与数据库中的原始数据保持一致性。 #### 实现方法 为了实现高效的缓存预热,在实际开发过程中可以采用多种策略和技术手段: 1. **多线程或多进程并行处理** 鉴于热数据量通常较大,单一线程或单一服务可能会导致效率低下。因此可以通过多个服务实例并行读取数据,并将其写入Redis缓存来提升性能[^2]。这种方式利用分布式架构的优势,显著缩短了整个预热的时间周期。 2. **基于业务逻辑筛选热数据** 并不是所有的数据都需要进行缓存预热,只有那些访问频率较高、查询耗时较长的关键数据才值得优先考虑。通过分析历史访问记录或其他指标,识别出这些热点数据集作为目标对象。 3. **编程接口支持复杂结构存储** 对于某些特殊场景下的需求,比如需要保存带有权重分值的关系型数据,则可借助像`zAdd()`这样的API函数完成向有序集合类型的键值对添加成员及其对应分数的操作[^3]。下面给出了一段具体的代码示例用于展示如何调用该功能: ```java public void zAdd(String key, Object value, double score) { ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); zset.add(key, value, score); } ``` 此片段定义了一个名为`zAdd`的方法,它接受三个参数——代表唯一标识符的字符串形式key、任意类型的value以及表示顺序依据的浮点数score;最终实现了针对指定Key下Value-Score映射关系的确立。 ### 注意事项 尽管上述提到的技术方案有助于提高系统的整体表现力,但在具体实施之前还需注意以下几点: - 数据同步问题:由于源端(通常是后台数据库)和目的端(Redis Cache之间可能存在延迟差异),所以要设计合理的刷新策略防止出现脏读现象。 - 资源消耗评估:大规模并发写入动作会对服务器造成额外负担,故而应当合理规划参与节点数量及每批次传输规模大小。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello.Reader

请我喝杯咖啡吧😊

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值