Redis 应用实践:缓存预热与缓存穿透解决方案

Redis 应用实践:缓存预热与缓存穿透解决方案

  • 一、简介
    • [1.1 简介](#1.1 简介)
    • [1.2 缓存预热 穿透](#1.2 缓存预热 穿透)
  • 二、缓存预热
    • [2.1 缓存预热基本原理](#2.1 缓存预热基本原理)
    • [2.2 Redis 缓存预热实现](#2.2 Redis 缓存预热实现)
      • [2.2.1 基于数据量预热](#2.2.1 基于数据量预热)
      • [2.2.2 基于时间预热](#2.2.2 基于时间预热)
      • [2.2.3 周期性预热](#2.2.3 周期性预热)
  • 三、缓存穿透
    • [3.1 缓存穿透基本原理](#3.1 缓存穿透基本原理)
    • [3.2 Redis 缓存穿透解决方案](#3.2 Redis 缓存穿透解决方案)
      • [3.2.1 布隆过滤器](#3.2.1 布隆过滤器)
      • [3.2.2 缓存空对象](#3.2.2 缓存空对象)
      • [3.2.3 限流](#3.2.3 限流)
  • 四、应用实践
    • [4.1 在 Spring Boot 中使用 Redis 缓存预热和缓存穿透解决方案](#4.1 在 Spring Boot 中使用 Redis 缓存预热和缓存穿透解决方案)
    • [4.2 在分布式系统中使用 Redis 缓存预热和缓存穿透解决方案](#4.2 在分布式系统中使用 Redis 缓存预热和缓存穿透解决方案)

一、简介

1.1 简介

Redis是一个用于数据缓存、消息代理、持久化存储的内存型数据库。Redis的特点是高性能、高并发、支持丰富的数据类型,可以实现多种应用场景。

1.2 缓存预热 穿透

缓存预热是在系统开始运行之前,将数据加入缓存中。这样在后续的请求中,可以直接从缓存中读取数据,提高了系统的性能和响应速度。

缓存穿透是指查询一个不存在的数据,这会导致大量请求直接打到数据库上,影响数据库的性能。缓存穿透可以通过在缓存层增加布隆过滤器等进行解决。

二、缓存预热

2.1 缓存预热基本原理

缓存预热的基本原理:程序启动或重启的时候,将需要经常访问的数据,提前加载到缓存当中,以便后续直接读取。

2.2 Redis 缓存预热实现

2.2.1 基于数据量预热

根据数据量的大小进行预热,比较常见的方法是在程序启动时,读取所有的数据,将数据全部写入缓存当中,以此实现缓存预热。其优点是预热完成后,可以避免缓存穿透;缺点是数据量大的时候,预热的时间较长。

2.2.2 基于时间预热

根据数据最近的更新时间和访问频率,对数据进行预热。比如最近7天读取频率比较高的数据,在程序启动时就可以进行预热。其优点是可以提高预热效率;缺点是无法避免缓存穿透。

2.2.3 周期性预热

周期性预热是指定期间内进行缓存预热,以保证系统的高效性。比如每天凌晨1点进行缓存预热,以此保证当系统高峰期到来时,能够有足够的缓存支持。其优点是预热时间可控,缺点是可能不能覆盖到所有的数据。

java 复制代码
public class RedisCachePreheating {

    /**
     * 缓存预热:基于数据量预热
     */
    public void preheatByDataSize(){
        // 读取所有数据
        List<Data> dataList = readAllData();
        for(Data data : dataList){
            // 将数据写入缓存
            writeToCache(data);
        }
    }
  
    /**
     * 缓存预热:基于时间预热
     */
    public void preheatByTime(){
        // 获取最近7天的数据列表
        List<Data> dataList = readDataByTime(7);
        for(Data data : dataList){
            // 将数据写入缓存
            writeToCache(data);
        }
    }

    /**
     * 缓存预热:周期性预热
     */
    public void periodPreheat(){
        // 每隔1小时预热一次
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                // 预热操作,类似于preheatByDataSize()或preheatByTime()
            }
        }, 0, 60 * 60 * 1000);
    }
}

三、缓存穿透

3.1 缓存穿透基本原理

缓存穿透是指当一个查询不存在于缓存中,而且每次查询也都不会被缓存时,就会直接访问数据库。如果出现大量查询结果不存在的情况,就可能导致数据库崩溃。缓存穿透的原因可能是因为查询的条件非常特殊或者恶意攻击。

3.2 Redis 缓存穿透解决方案

以下是常见的 Redis 缓存穿透解决方案:

3.2.1 布隆过滤器

布隆过滤器是一种内存型、不可逆的数据结构。它使用哈希函数来判断一个元素是否在集合中。因为它的计算量小且运行速度快,所以通常被用作解决缓存穿透和大数据去重等问题。

在 Redis 中,我们可以使用 RedisBloom 模块来实现布隆过滤器。

Java 实现布隆过滤器的代码示例:

java 复制代码
// 创建布隆过滤器并将其添加到 Redis 中
Jedis jedis = new Jedis("localhost", 6379);
RedisBloomFilter<Integer> bloomFilter = RedisBloomFilter.create(jedis, "bloom", 1000, 0.01);
bloomFilter.add(42);
// 检查元素是否存在于集合中
bloomFilter.contains(42); // 返回 true
bloomFilter.contains(666); // 返回 false

3.2.2 缓存空对象

当缓存查询结果为空时,我们可以将这个空对象添加到缓存中。这样下次同样的查询就会命中缓存,而不用去访问数据库了。

Java 实现缓存空对象的代码示例:

java 复制代码
// 查询缓存。
Object result = redisTemplate.opsForValue().get(key);
if(result == null) {
    // 查询数据库。
    result = dao.query(key);
    // 将查询结果添加到缓存,有效期设置为 5 分钟。
    redisTemplate.opsForValue().set(key, result, Duration.ofMinutes(5));
}

3.2.3 限流

使用限流可以防止恶意攻击。

可以使用 Redis 的计数器和时间窗口算法来将高并发请求控制在一个较低的速率内。

Java 实现简单限流的代码示例:

java 复制代码
// 获取当前时间戳。
long now = Instant.now().getEpochSecond();
// 在 Redis 中记录这 1 秒钟内的请求次数。
long current = redisTemplate.opsForValue().increment(key, 1);
// 设置有效期为 1 秒钟。
redisTemplate.expire(key, 1, TimeUnit.SECONDS);
if(current > maxRequests) {
    // 请求次数超限,返回失败。
    return new Response(false, "too many requests");
} else {
    // 请求次数未超限。
    // 如果是第一个请求,设置过期时间为 1 秒钟。
    redisTemplate.opsForValue().setIfAbsent(key, "", Duration.ofSeconds(1));
    return new Response(true, "");
}

四、应用实践

4.1 在 Spring Boot 中使用 Redis 缓存预热和缓存穿透解决方案

在 Spring Boot 中,我们可以使用注解来实现缓存预热和缓存穿透解决方案。

首先,我们需要添加 Spring Cache 和 Redis 相关的依赖,并配置 RedisTemplate 和缓存管理器。然后,我们可以在 Service 层的方法上使用 @Cacheable 注解来开启缓存,例如:

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    @Autowired
    private RedisTemplate<String, User> redisTemplate;

    @Cacheable(value = "user", key = "#id")
    public User get(int id) {
        // 直接从数据库中获取用户。
        return userDao.get(id);
    }
}

4.2 在分布式系统中使用 Redis 缓存预热和缓存穿透解决方案

在分布式系统中,我们可以使用 Redisson 或者其他类似的分布式锁来避免重复预热和处理缓存穿透。

例如,我们可以在所有服务器都停止服务前,将缓存数据写入 Redis 中,并加上分布式锁来保证只有一个服务能够进行预热。另外,我们也可以使用分布式锁来避免缓存穿透导致的数据库崩溃等问题。

相关推荐
编程、小哥哥6 分钟前
设计模式之抽象工厂模式(替换Redis双集群升级,代理类抽象场景)
redis·设计模式·抽象工厂模式
洛卡卡了28 分钟前
从单层到 MVC,再到 DDD:架构演进的思考与实践
架构·mvc
Dreams°1231 小时前
大数据 ETL + Flume 数据清洗 — 详细教程及实例(附常见问题及解决方案)
大数据·单元测试·可用性测试
sf_www1 小时前
Flink on YARN是如何确定TaskManager个数的
大数据·flink
乌恩大侠1 小时前
O-RAN Fronthual CU/Sync/Mgmt 平面和协议栈
5g·平面·fpga开发·架构
武子康2 小时前
大数据-213 数据挖掘 机器学习理论 - KMeans Python 实现 距离计算函数 质心函数 聚类函数
大数据·人工智能·python·机器学习·数据挖掘·scikit-learn·kmeans
武子康2 小时前
大数据-214 数据挖掘 机器学习理论 - KMeans Python 实现 算法验证 sklearn n_clusters labels
大数据·人工智能·python·深度学习·算法·机器学习·数据挖掘
龙哥·三年风水3 小时前
群控系统服务端开发模式-应用开发-前端框架
分布式·vue·群控系统
PGCCC3 小时前
【PGCCC】postgresql 缓存池并发设计
数据库·缓存·postgresql
Aloudata3 小时前
NoETL自动化指标平台为数据分析提质增效,驱动业务决策
大数据·数据分析·指标平台·指标体系