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 节点或分布式服务中,减少单点压力。

对比总结

问题 定义 原因 解决方案
缓存雪崩 大量缓存数据同时失效,导致请求集中打到数据库或服务 缓存失效时间一致,缓存服务宕机 错峰失效、缓存预热、高可用集群、限流降级
缓存穿透 请求的键既不在缓存中,也不在数据库中 恶意攻击、数据不全 布隆过滤器、空值缓存、参数校验、限流防护
缓存击穿 热点数据缓存失效时,大量请求同时查询该数据 热点数据失效,大量请求集中到数据库 互斥锁机制、逻辑过期、请求分流、分布式限流
相关推荐
Pandaconda2 小时前
【Golang 面试题】每日 3 题(二十一)
开发语言·笔记·后端·面试·职场和发展·golang·go
想要入门的程序猿2 小时前
Qt菜单栏、工具栏、状态栏(右键)
开发语言·数据库·qt
键盘上的蚂蚁-3 小时前
Python 语言结合 Flask 框架来实现一个基础的代购商品管理
jvm·数据库·oracle
代码欢乐豆4 小时前
MongoDB的部署和操作
数据库·mongodb
快乐非自愿4 小时前
一文解秘Rust如何与Java互操作
java·开发语言·rust
小万编程4 小时前
基于SpringBoot+Vue毕业设计选题管理系统(高质量源码,提供文档,免费部署到本地)
java·vue.js·spring boot·计算机毕业设计·java毕业设计·web毕业设计
m0_748235074 小时前
使用rustDesk搭建私有远程桌面
java
<e^πi+1=0>4 小时前
使用Locust对MongoDB进行负载测试
数据库·mongodb
快乐是4 小时前
发票打印更方便
java
文浩(楠搏万)4 小时前
Java内存管理:不可达对象分析与内存泄漏优化技巧 Eclipse Memory Analyzer
java·开发语言·缓存·eclipse·内存泄漏·不可达对象·对象分析