redis之缓存预热,缓存雪崩,缓存击穿,缓存穿透

目录

redis缓存预热

redis缓存雪崩

redis缓存击穿

redis缓存穿透


redis缓存预热

        Redis 缓存预热是指在系统启动或者缓存失效后,提前将部分数据加载到 Redis 缓存中,以便用户在访问时能够快速获取数据,提高系统性能和响应速度。

  • 全量加载: 在系统启动时,将所有需要缓存的数据从数据库或其他数据源加载到 Redis 中,确保缓存中包含了所有可能被访问的数据。这种方式适用于数据量较小、变动频率低的场景。

  • 按需加载: 根据访问模式和数据访问频率,预先加载部分热门数据到 Redis 缓存中,以及时满足用户请求。这种方式适用于数据量较大、热点数据集中的场景。

  • 定时加载: 设置定时任务,在系统空闲时段或低峰期,定期从数据库加载数据到 Redis 缓存中,以确保缓存数据的及时性和完整性。

  • 异步加载: 在系统启动后,通过异步任务的方式逐步将数据加载到 Redis 缓存中,避免系统启动时的压力过大。

redis缓存雪崩

        缓存雪崩是指在某个时间段,缓存中大量的数据同时失效或者因为某种原因都不可用,导致大量请求直接落到了后端数据库上,压力过大而导致数据库或其他服务崩溃,最终影响整个系统的稳定性和可用性。

缓存雪崩一般发生在以下情况:

  • 缓存服务器宕机或故障
  • 所有缓存数据在同一时间失效
  • 缓存数据没有设置过期时间或者过期时间过短
  • 大量请求访问热门数据,导致缓存击穿,无法承受巨大的并发请求

避免缓存雪崩,可以采取以下措施:

  • 分布式缓存: 将缓存分散到多个节点上,避免所有节点同时失效导致缓存雪崩。比如使用 Redis Cluster、Memcached Cluster 等分布式缓存方案。

  • 缓存预热: 在系统启动时,预先将热门数据加载到缓存中,避免缓存全部失效时,大量请求落到数据库上。

  • 设置过期时间: 设置合理的缓存过期时间,避免所有缓存同时失效。

  • 限流降级: 当请求量超过系统承受范围时,通过限流或者降级等措施,分流请求,保证系统的稳定性和可用性。

  • 多级缓存: 使用多级缓存架构,将热门数据放到内存中的缓存,避免频繁读取数据库和外部接口。

redis缓存击穿

        缓存击穿是指当某个热点数据的缓存过期或不存在时,大量请求同时访问该数据,导致请求直接落到了后端数据库上,造成数据库压力过大,影响系统性能和可用性。

缓存击穿一般发生在以下情况:

  • 热点数据缓存失效:由于缓存的过期时间到达或被手动删除等原因,导致热点数据不再存在于缓存中。
  • 并发请求访问热点数据:大量并发请求同时访问热点数据,此时缓存未命中,导致请求直接访问数据库。

为了避免缓存击穿,可以采取以下措施:

  1. 加锁机制: 当检测到缓存失效时,先进行加锁操作,只允许一个请求去查询数据库,其他请求等待结果。查询完毕后,更新缓存并释放锁,其他请求再从缓存获取数据。

  2. 设置短暂的空值缓存: 在缓存失效时,先将空值(null)或者占位数据设置到缓存中,并给予较短的过期时间,避免大量请求同时访问数据库。同时,在缓存中设置一个较长的过期时间,以防止后续请求再次击穿。

  3. 异步更新缓存: 当检测到缓存失效后,异步更新缓存,而不是在请求时即时更新缓存。这样可以避免大量请求同时访问数据库,在缓存更新完成后,后续请求再从缓存获取数据。

  4. 使用互斥锁或分布式锁: 在多服务器环境下,使用互斥锁或者分布式锁来保证只有一个请求去查询数据库,其他请求等待结果,避免大量请求同时访问数据库。

redis缓存穿透

        缓存穿透是指恶意请求或者不存在的数据被请求,导致缓存无法命中,每次请求都直接访问数据库或其他数据源,造成数据库压力过大。

缓存穿透一般发生在以下情况:

  • 恶意请求:恶意用户发送的请求,例如使用遍历查询参数、非法字符等,导致缓存无法命中,直接访问数据库。
  • 数据不存在:请求查询的数据本身就不存在于数据库或其他数据源中,无论怎样的请求都无法在缓存中命中。

为了避免缓存穿透,可以采取以下措施:

  1. 布隆过滤器(Bloom Filter): 使用布隆过滤器来预先过滤掉不存在的数据,当请求到来时,首先经过布隆过滤器的检查,如果不在布隆过滤器中,则直接拒绝请求,避免直接访问数据库。

  2. 空对象缓存: 当查询数据库为空时,在缓存中设置一个空对象(如null),并设置较短的过期时间,避免下次请求再次穿透到数据库。

  3. 合法性检查: 对于用户发送的请求参数进行合法性检查,避免恶意请求导致缓存穿透,可以使用验证 token、接口鉴权等方式进行有效性验证。

  4. 限流和监控: 对请求进行限流,避免大量的恶意请求进入系统,同时建立监控系统,及时发现异常请求并进行处理。

 

空对象缓存解决缓存穿透

    public Customer findCustomerById(Integer customerId) {
        Customer customer = null;
        // 缓存redis的key名称
        String key = CACHE_KEY_CUSTOMER + customerId;
        // 1.去redis上查询
        customer = (Customer) redisTemplate.opsForValue().get(key);

        // 2. 如果redis有,直接返回  如果redis没有,在mysql上查询
        if (customer == null) {
            // 3.对于高QPS的优化,进来就先加锁,保证一个请求操作,让外面的redis等待一下,避免击穿mysql(大公司的操作 )
            synchronized (CustomerService.class) {
                // 3.1 第二次查询redis,加锁后
                customer = (Customer) redisTemplate.opsForValue().get(key);
                // 4.再去查询我们的mysql
                customer = customerMapper.selectByPrimaryKey(customerId);

                // 5.mysql有,redis无
                if (customer != null) {
                    // 6.把mysql查询到的数据会写到到redis, 保持双写一致性  7天过期
                    redisTemplate.opsForValue().set(key, customer, 7L, TimeUnit.DAYS);
                }else {
                    // defaultNull 规定为redis查询为空、MySQL查询也没有,缓存一个defaultNull标识为空,以防缓存穿透
                    redisTemplate.opsForValue().set(key, "defaultNull", 7L, TimeUnit.DAYS);
                }
            }
        }
        return customer;
    }

Google布隆过滤器Guava解决缓存穿透

添加pom文件

        <!--guava Google 开源的 Guava 中自带的布隆过滤器-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>

业务类

  • GUavaBloomFilterController
import com.xfcy.service.GuavaBloomFilterService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;


@Api(tags = "gogle工具Guava处理布隆过滤器")
@RestController
@Slf4j
public class GuavaBloomFilterController {

    @Resource
    private GuavaBloomFilterService guavaBloomFilterService;

    @ApiOperation("guava布隆过滤器插入100万样本数据,额外10w(110w)测试是否存在")
    @RequestMapping(value = "/guavafilter", method = RequestMethod.GET)
    public void guavaBloomFilter() {
        guavaBloomFilterService.guavaBloomFilter();
    }
}
  • GUavaBloomFilterService 
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;


@Slf4j
@Service
public class GuavaBloomFilterService {
    // 1.定义一个常量
    public static final int _1W = 10000;
    // 2.定义我们guava布隆过滤器,初始容量
    public static final int SIZE = 100 * _1W;
    // 3.误判率,它越小误判的个数也越少(思考:是否可以无限小? 没有误判岂不是更好)
    public static double fpp = 0.01;  // 这个数越小所用的hash函数越多,bitmap占用的位越多  默认的就是0.03,5个hash函数   0.01,7个函数
    // 4.创建guava布隆过滤器
    private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), SIZE, fpp);

    public void guavaBloomFilter() {
        // 1.先让bloomFilter加入100w白名单数据
        for (int i = 0; i < SIZE; i++) {
            bloomFilter.put(i);
        }
        // 2.故意取10w个不在合法范围内的数据,来进行误判率的演示
        ArrayList<Integer> list = new ArrayList<>(10 * _1W);

        // 3.验证
        for (int i = SIZE + 1; i < SIZE + (10 * _1W); i++){
            if (bloomFilter.mightContain(i)){
                log.info("被误判了:{}", i);
                list.add(i);
            }
        }
        log.info("误判总数量:{}", list.size());
    }
}

 

缓存问题总结

缓存问题 产生原因 解决方案
缓存更新不一致 数据变更、缓存时效性 同步更新、失效更新、异步更新、定时更新
缓存不一致 同步更新失败、异步更新 增加重试、补偿任务、最终一致
缓存穿透 恶意攻击 空对象缓存、bloomFilter 过滤器
缓存击穿 热点key失效 互斥更新、随即退避、差异失效时间
缓存雪崩 缓存挂掉 快速失败熔断、主从模式、集群模式

相关推荐

  1. Redis缓存击穿缓存雪崩缓存穿透

    2024-02-16 07:48:01       56 阅读
  2. Redis缓存穿透缓存击穿缓存雪崩、无底洞

    2024-02-16 07:48:01       35 阅读
  3. Redis缓存穿透击穿雪崩

    2024-02-16 07:48:01       43 阅读
  4. Redis缓存穿透击穿雪崩

    2024-02-16 07:48:01       32 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-16 07:48:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-16 07:48:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-02-16 07:48:01       82 阅读
  4. Python语言-面向对象

    2024-02-16 07:48:01       91 阅读

热门阅读

  1. leetcode - 2149. Rearrange Array Elements by Sign

    2024-02-16 07:48:01       49 阅读
  2. mac安装docker-compose

    2024-02-16 07:48:01       60 阅读
  3. C语言-----习题

    2024-02-16 07:48:01       47 阅读
  4. 罗马数字转整数

    2024-02-16 07:48:01       61 阅读
  5. 深度学习代码块之计算模型参数量和显存大小

    2024-02-16 07:48:01       56 阅读
  6. 使用深度学习进行序列分类

    2024-02-16 07:48:01       52 阅读
  7. uniapp 读取本地文件

    2024-02-16 07:48:01       64 阅读
  8. 【图论经典题目讲解】CF715B - Complete The Graph

    2024-02-16 07:48:01       56 阅读