Redis数据库——Redis雪崩、穿透、击穿

本文先简要介绍为什么需要使用Redis,以及过期键的删除策略,进而详细介绍Redis雪崩、穿透、击穿的发生场景和解决方案。

文章目录

前情提要

为什么使用redis?

  1. 提高性能:缓存査询速度比数据库查询速度快(内存Vs硬盘);
  2. 提高井发能力:缓存分担了部分请求,支持更高的并发。

Redis 的过期键删除策略

Redis 为管理内存,对设置了过期时间的键采用了以下三种删除策略:

  1. 定时过期

    • 描述:为每个设置了过期时间的键创建一个定时器,到达过期时间立即清除。
    • 优点:对内存很友好,过期数据能及时清除。
    • 缺点:需要消耗大量的 CPU 资源来处理定时器,影响缓存响应时间和吞吐量。
  2. 惰性过期

    • 描述:只有在访问某个键时才判断其是否过期,过期则清除。
    • 优点:最大化节省 CPU 资源。
    • 缺点:可能会有大量过期键未被访问而占用内存。
  3. 定期删除

    • 描述 :每隔一定时间扫描 expires 字典中的部分键,清除过期键。
    • 优点:折中策略,通过调整扫描时间间隔和每次扫描数量,平衡 CPU 和内存资源。

默认使用惰性过期 (Lazy Expiration)和定期删除 (Periodic Deletion)两者结合的方式,能够在性能和内存使用之间找到一个良好的平衡。

数据读取流程

Redis 的数据读取通常遵循以下步骤:

  1. 缓存命中:如果数据在缓存中存在,直接从缓存中读取。
  2. 缓存未命中:如果缓存中没有数据,则查询数据库,获取数据后写入缓存供下次使用。

三种问题及解决方案

  • 缓存雪崩:主要解决缓存大规模失效问题,核心在于错峰和高可用。
  • 缓存穿透:主要解决请求不存在数据的问题,核心在于布隆过滤器和空值缓存。
  • 缓存击穿:主要解决热点数据失效问题,核心在于互斥锁和逻辑过期。

1. 缓存雪崩

定义 :在某一时间段内,缓存集中过期或 Redis 服务宕机,导致大量请求直接涌向数据库,可能引发数据库崩溃。

原因

内存昂贵且有限,所以Redis需要给数据设置过期时间,将过期键数据删除。

  • 缓存中的大量数据设置了相同的过期时间,导致集中失效。
  • 缓存服务宕机或崩溃,所有缓存失效。

解决方案

  1. 键值------缓存失效时间错峰

    • 给缓存的过期时间增加一个随机值,减少同一时间段的缓存同时过期。
    bash 复制代码
    SET key value EX 300 + random(60)  # 过期时间加随机值
  2. 事发前------Redis 高可用架构

    • 实现 Redis 主从架构或 Redis Cluster,避免单点故障。
    • 使用 Sentinel(哨兵)进行故障转移,尽量避免Redis挂掉这种情况发生。
  3. 事发中------本地缓存和限流

    • 在 Redis 宕机时,启用本地缓存(如 Ehcache)。
    • 通过限流(如 Hystrix)保护数据库(起码能保证我们的服务还是能正常工作的)。
  4. 事发后------Redis 持久化

    • 开启 RDB 或 AOF 持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。

2. 缓存穿透

定义:用户请求的键在缓存和数据库中都不存在,导致每次请求都会直接访问数据库。

为什么说键在数据库中也不存在 呢?

因为如果键在数据库中存在,第一次命中,就会写入缓存,下次就不会请求到数据库,不属于缓存穿透

原因

  • 恶意攻击:通过大量请求不存在的键对系统进行攻击。例如 ID 为负数或不存在的用户数据。
  • 数据不全:一些正常请求因为数据未命中而直接访问数据库。

解决方案

  1. 布隆过滤器

    • 在缓存前引入布隆过滤器,用于判断请求的键是否可能 存在:
      • 如果布隆过滤器判断不存在,直接返回,无需访问缓存和数据库。
      • 如果布隆过滤器判断可能存在,再访问缓存或数据库。
    bash 复制代码
    # 添加数据到布隆过滤器
    BF.ADD filter "valid_key"
    # 查询数据是否存在
    BF.EXISTS filter "invalid_key"  # 返回 false,直接拦截请求
  2. 缓存空值

    • 当查询结果为空时,将空值存入缓存,并设置较短的过期时间。
    bash 复制代码
    SET key "null" EX 60  # 缓存空值 60 秒

    这种情况我们一般会将空对象设置一个较短的过期时间(时间太长的话,万一数据库中已经有数据了,但是长时间命中redis发现没有数据,导致用户拿不到数据)。

  3. 参数校验

    • 对输入的参数进行校验,避免无效请求到达后端。
  4. 限流防护

    • 对频繁请求不存在键的客户端进行限制,防止恶意攻击。

3. 缓存击穿

定义 :当缓存中某个高频访问的热点键过期时,大量请求同时到达数据库,造成数据库压力骤增。

场景

  • 一个热点数据被大量访问,且缓存突然失效,失效后请求全部打到数据库。

解决方案

  1. 热点键永不过期

    • 热点键不设置过期时间,直接通过覆盖更新来保持数据一致性。
  2. 加互斥锁

    • 使用 Redis 或 Zookeeper 的分布式锁 ,控制只有一个线程能访问数据库并更新缓存,其余线程等待。
    bash 复制代码
    SET lock:key "unique_value" NX EX 10  # 设置分布式锁
  3. 热点键限流

    • 对热点键的请求进行限流,避免短时间内大量请求涌入。
  4. 热点数据分片

    • 将热点数据分散到不同的 Redis 节点或分布式服务中,减少单点压力。

对比总结

问题 定义 原因 解决方案
缓存雪崩 大量缓存数据同时失效,导致请求集中打到数据库或服务 缓存失效时间一致,缓存服务宕机 错峰失效、缓存预热、高可用集群、限流降级
缓存穿透 请求的键既不在缓存中,也不在数据库中 恶意攻击、数据不全 布隆过滤器、空值缓存、参数校验、限流防护
缓存击穿 热点数据缓存失效时,大量请求同时查询该数据 热点数据失效,大量请求集中到数据库 互斥锁机制、逻辑过期、请求分流、分布式限流
相关推荐
AAA修煤气灶刘哥19 分钟前
别让Redis「歪脖子」!一次搞定数据倾斜与请求倾斜的捉妖记
redis·分布式·后端
AAA修煤气灶刘哥33 分钟前
后端人速藏!数据库PD建模避坑指南
数据库·后端·mysql
程序新视界1 小时前
学习MySQL绕不开的两个基础概念:聚集索引与非聚集索引
mysql
架构师沉默3 小时前
设计多租户 SaaS 系统,如何做到数据隔离 & 资源配额?
java·后端·架构
RestCloud4 小时前
跨境数据传输:ETL如何处理时区与日期格式差异
mysql·api
Java中文社群5 小时前
重要:Java25正式发布(长期支持版)!
java·后端·面试
RestCloud5 小时前
揭秘 CDC 技术:让数据库同步快人一步
数据库·api
每天进步一点_JL6 小时前
JVM 类加载:双亲委派机制
java·后端
用户298698530146 小时前
Java HTML 转 Word 完整指南
java·后端
渣哥6 小时前
原来公平锁和非公平锁差别这么大
java