预热
本文最后更新于 317 天前,其中的信息可能已经有所发展或是发生改变。

预热一般指缓存预热,一般用在高并发系统中,为了提升系统在高并发情况下的稳定性的一种手段。

缓存预热是指在系统启动之前或系统达到高峰期之前,通过预先将常用数据加载到缓存中,以提高缓存命中率系统性能的过程。缓存预热的目的是尽可能地避免缓存击穿和缓存雪崩,还可以减轻后端存储系统地负载,提高系统地响应速度和吞吐量。

预热地必要性

缓存预热地好处有很多,如:

  1. 减少冷启动影响:当系统重启或新启动时,缓存是空地,这被称为冷启动。冷启动可能导致首次请求处理缓慢,因为数据需要从慢速存储(如数据库)检索
  2. 提高数据访问速度:通过预先加载常用数据到缓存中,可以确保数据快速可用,从而加快数据访问速度
  3. 平滑数据峰值(削峰填谷):在流量高峰期之前预热缓存可以帮助系统更好地处理高流量,避免在流量激增时出现性能下降
  4. 保证数据地时效性:定期预热可以保证缓存中地数据是最新地,特别是对于高度依赖于实时数据地系统。
  5. 减少对后端系统地压力:通过缓存预热,可以减少对数据库或其他后端服务的直接查询,从而减轻它们的负载

预热的方法

缓存预热的一般做法是在系统启动或系统空闲期间,将常用的数据加载到缓存中,抓哟做法有以下几种:

1)系统启动时加载

在系统启动时,将常用的数据加载到缓存中,以便后续的访问可以直接从缓存中获取。

2)定时任务加载

定时执行任务,将常用的数据加载到缓存中,以保持缓存中数据的实时性和准确性。

3)手动触发加载

在系统达到高峰期之前,手动触发加载常用数据到缓存中,以提高缓存命中率和系统性能。

4)用时加载

在用户请求到来时,根据用户的访问模式和业务需求,动态地将数据加载到缓存中。

5)缓存加载器

一些缓存框架提供了缓存加载器地机制,可以在缓存中不存在数据时,自动调用加载器加载数据到缓存中。

Redis 预热

在分布式缓存中,我们通常都是使用 Redis,针对 Redis 地预热,有以下几个工具可供使用,帮助我们实现缓存地预热:

RedisBloom

RedisBloom 是 Redis 地一个模块,提供了多个数据结构,包括布隆过滤器、计数器、和 TopK 数据结构等。其中,布隆过滤器可以用于 Redis 缓存预热,通过将预热数据添加到布隆过滤器中,可以快速判断一个键是否存在于缓存中。

Redis Bulk Loading

这是一个官方出的,基于 Redis 协议批量写入数据地工具

Redis Desktop Manager

Redis Desktop Manager 是一个图形化地 Redis 客户端,可以用于管理 Redis 数据库和精选缓存预热。通过 Redis Desktop Manager,可以轻松地预热数据批量导入到 Redis 缓存中。

应用启动时预热

ApplicationReadyEvent

在应用程序启动时,可以通过监听应用启动事件,或者在应用地初始化阶段,将需要缓存地数据加载到缓存中。

ApplicationReadyEvent 是 SpringBoot 框架中地一个事件类,它表示应用程序已经准备好接受请求,即应用程序已启动且上下文已刷新。这个事件是在ApplicationContext 被初始化和刷新,并且应用程序已经准备好处理请求时触发的。

基于ApplicationReadyEvent,我们可以在应用程序完全启动并处于可用状态后执行一些初始化逻辑,使用@EvenListener 注解或实现 ApplicationListener 接口来监听这个事件。例如,使用 @EventListener 注解:

@EventListener(ApplicationReadyEvent.class)
public void preloadCache() {
    // 在应用启动后执行缓存预热逻辑
    // ...
}

Runner

如果你不想直接监听 ApplicationReadyEvent,在 SpringBoot 中,也可以通过CommandLineRunner 和 ApplicationRunner 来实现这个功能。

CommandLineRunner 和 ApplicationRunner 是SpringBoot 中用于在应用程序启动后执行特定逻辑的接口。这解释听上去就像是专门干这个事儿的。

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class MyCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        // 在应用启动后执行缓存预热逻辑
        // ...
    }
}
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 在应用启动后执行缓存预热逻辑
        // ...
    }
}

CommandLineRunner 和 ApplicationRunner的调用,是在SpringApplication的run方法中

其实就是callRunners(context, applicationArguments);的实现:

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

使用 InitializingBean 接口

实现 InitializingBean 接口,并在 afterPropertiesSet 方法中执行缓存预热的逻辑。这样,Spring 在初始化 Bean 时会调用 afterPropertiesSet 方法。

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class CachePreloader implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        // 执行缓存预热逻辑
        // ...
    }
}

使用 @PostConstruct 注解

类似的,我们还可以使用 @PostConstruct 注解标注一个方法,该方法将在 Bean 的构造函数执行完毕后立即被调用。在这个方法中执行缓存预热的逻辑。

import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;

@Component
public class CachePreloader {

    @PostConstruct
    public void preloadCache() {
        // 执行缓存预热逻辑
        // ...
    }
}

定时任务预热

在启动过程中预热有一个问题,那就是一旦启动之后,如果需要预热新的数据,或者需要修改数据,就不支持了,那么,在应用的运行过程中,我们也是可以通过定时任务来实现缓存的更新预热的。

我们通常依赖这种方式来确保缓存中的数据是最新的,避免因为业务数据的变化而导致缓存数据过时。

在 Spring 中,想要实现一个定时任务也挺简单的,基于 @Scheduled 就可以轻易实现.

@Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
public void scheduledCachePreload() {
    // 执行缓存预热逻辑
    // ...
}

缓存器预热

些缓存框架提供了缓存加载器的机制,可以在缓存中不存在数据时,自动调用加载器加载数据到缓存中。这样可以简化缓存预热的逻辑。如Caffeine中就有这样的功能:

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class MyCacheService {

    private final LoadingCache<String, String> cache;

    public MyCacheService() {
        this.cache = Caffeine.newBuilder()
                .refreshAfterWrite(1, TimeUnit.MINUTES)  // 配置自动刷新,1分钟刷新一次
                .build(key -> loadDataFromSource(key));  // 使用加载器加载数据
    }

    public String getValue(String key) {
        return cache.get(key);
    }

    private String loadDataFromSource(String key) {
        // 从数据源加载数据的逻辑
        // 这里只是一个示例,实际应用中可能是从数据库、外部服务等获取数据
        System.out.println("Loading data for key: " + key);
        return "Value for " + key;
    }
}

在上面的例子中,我们使用 Caffeine.newBuilder().refreshAfterWrite(1, TimeUnit.MINUTES) 配置了缓存的自动刷新机制,即每个缓存项在写入后的1分钟内,如果有读请求,Caffeine 会自动触发数据的刷新。

loadDataFromSource 方法是用于加载数据的自定义方法。你可以在这个方法中实现从数据源(例如数据库、外部服务)加载数据的逻辑。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇