Redis如何实现缓存预热功能

Java技术 潘老师 7个月前 (10-12) 231 ℃ (0) 扫码查看

在业务系统中,为了降低对业务数据库的负载压力,我们需要在程序启动时将一些常用数据主动加载到内存数据库,这就是我们通常所说的缓存预热策略。

官方定义如下:

缓存预热是一种策略,它在程序启动或缓存失效后主动将热点数据加载到缓存中。通过这种方式,当实际请求到达程序时,热点数据已经存在于缓存中,从而有助于减少缓存穿透和缓存击穿的情况,同时减轻了SQL服务器的负载。

为了实现这一策略,我们结合业务系统并进行了相关设计,以下是设计代码的详细描述:

1.定义缓存抽象类

定义了一个抽象类,用于执行缓存操作,包括初始化缓存、从缓存中获取数据、清理缓存以及刷新缓存等操作。这个抽象类与Spring Boot的生命周期监控相关联。

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

2.spring boot生命周期的监控

在Spring Boot项目启动后,立即执行初始化缓存操作。而在项目结束时,缓存被立即删除。

@Component
public class MyFullLifecycle implements SmartLifecycle {
    @Resource
    private ApplicationContext applicationContext;
    private boolean isRunning = false;

    public AbstractCache getAbstractCache() {
        Map<String, AbstractCache> beansOfType = applicationContext.getBeansOfType(AbstractCache.class);
        for (Map.Entry<String, AbstractCaches> cacheEntry : beansOfType.entrySet()) {
            return applicationContext.getBean(cacheEntry.getValue().getClass());
        }
        return null;
    }

    @Override
    public void start() {
        //编写程序启动时的逻辑
        System.out.println("应用程序启动");
        isRunning = true;
        AbstractCache cache = getAbstractCache();
        cache.init();
    }

    @Override
    public void stop() {
        //编写程序停止时的逻辑
        System.out.println("应用程序停止");
        isRunning = false;
        AbstractCache cache = getAbstractCache();
        cache.clear();
    }

    @Override
    public boolean isRunning() {
        return isRunning;
    }

}

3.创建AbstractCache的具体实现类

创建了继承该抽象类的具体实现类,其中包括以下核心方法的重写:

  • 初始化:将所有热点数据缓存到Redis中。
  • 查询:如果缓存中不存在特定key的数据,就执行初始化缓存;否则,直接从缓存中获取数据。
  • 删除:用于在服务关闭时清除缓存。这里采用了直接删除key的方法,但对于大量key的情况,建议使用游标或Lua脚本来删除。
@Component
@RequiredArgsConstructor
public class UserCache extends AbstractCache {
    private static final String USERS_KEY = "users" ;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private UserService userService;
    @Override
    protected void init() {
        if (Boolean.FALSE.equals(redisTemplate.hasKey(USERS_KEY))) {
            redisTemplate.opsForValue().set(USERS_KEY, userService.list(), 30, TimeUnit.MINUTES);
            
        }
    }
    @Override
    public <T> T get() {
        if (Boolean.FALSE.equals(redisTemplate.hasKey(USERS_KEY))){
            init();
        }
        return (T) redisTemplate.opsForValue().get(USERS_KEY);
    }
    @Override
    public void clear() {
        redisTemplate.delete(USERS_KEY);
    }
}

4.测试

提供了一个接口类,用于测试实现效果。

@GetMapping(value = "users ")
public List<User> getUsers() {
    return userCache.get();
}

以上核心的代码完成后我们启动服务测试一下效果:

相关的时间段redis的日志如下图,当服务启动之后,缓存中就有了数据,接口测试可以直接拿到相关数据;当服务关闭之后,缓存数据也一起的清空了。

需要注意的是,这种设计方式适用于单机模式,而对于多实例和分布式服务,必须考虑数据同步的问题。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/java/9465.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】