Redis-缓存更新策略

缓存穿透

本质原因

缓存穿透是指查询一个在缓存层和数据库层均不存在的数据。由于数据缺失,缓存无法命中,请求将直接穿透缓存层,由底层的数据库进行处理。如果此类请求并发量大,会导致数据库连接耗尽甚至宕机。

解决方案

缓存空对象

当数据库查询返回空结果时,仍将该 Key 存入缓存,其 Value 设为特定的标识(如 null),并设置较短的过期时间。

  • 实现逻辑: SET key "null" EX 60
  • 权衡分析:
    • 优势: 实现简单,能够有效拦截重复键值的非法请求。
    • 劣势: 若攻击者构造的 Key 离散度极高且不重复,会产生大量空值缓存,挤占内存空间并可能触发淘汰机制(LRU/LFU),导致热点数据被误踢。
布隆过滤器

在缓存层前置一个位图索引,记录所有合法数据的 Hash 特征。

  • 判定逻辑:
    • 判定不存在: 该数据一定不存在,直接拦截并返回。
    • 判定存在: 该数据可能存在(受哈希冲突影响),允许进入缓存/数据库层查询。
  • 权衡分析:
    • 优势: 内存效率极高,O(1) 的查询时间复杂度。
    • 劣势: 存在微小误判率;标准实现不支持删除操作,数据更新维护成本较高。

重点

  • 核心定义: 必须明确指出穿透是"缓存和数据库双缺失"的情况,以此区分缓存击穿(热点 Key 失效)和缓存雪崩(大规模 Key 同时失效)。
  • 防御深度: 面试时应强调"多级防御",第一道防线应在 接口层 (Controller) 进行严格的参数校验(如布隆过滤器前的正则校验、合法 ID 段检查)。
  • 布隆过滤器特性: 重点提及"判定不存在则一定不存在"这一特性,这是其作为拦截器的核心价值点。
  • 场景选型: * 若 Key 重复率高,优先选择缓存空对象
    • 若 Key 数量庞大且高度离散,优先选择布隆过滤器

缓存雪崩

缓存雪崩是指在极短的时间内,大量的缓存 Key 同时失效 ,或者 Redis 服务宕机,导致原本应该访问缓存的巨量请求瞬间全部涌入数据库。数据库因无法承受瞬时高并发压力而导致连接耗尽、响应变慢甚至崩溃,引发系统链式反应(级联故障)。

产生原因

  • Key 集中过期: 批量导入数据时设置了相同的过期时间(TTL)。
  • Redis 实例宕机: 缓存层整体瘫痪,请求全部透传。
  • 内存溢出淘汰: 缓存内存达到上限,触发淘汰策略,大量热点数据被同时清理。

解决方案

1. 过期时间增加随机扰动

在设置 TTL 时,在基础过期时间上增加一个随机偏移量(如 1-5 分钟的随机值)。

  • 实现逻辑: set(key, value, 3600 + random.nextInt(300))
  • 效果: 强制将原本集中的失效点在时间轴上"打散",避免请求峰值重叠。
2. 互斥锁

在缓存失效后,不立即加载数据库,而是先通过 setnx 尝试获取分布式锁。

  • 实现逻辑: 只有获取到锁的线程负责查询数据库并回写缓存,其他线程等待或重试。
  • 权衡: 虽能绝对保护数据库,但会降低系统的瞬时吞吐量,增加了代码复杂度。
3. 逻辑过期

不使用 Redis 物理过期,而是在 Value 中存储过期时间字段。

  • 实现逻辑:
    1. 线程发现数据逻辑过期,尝试获取锁。
    2. 获取锁成功,开启新线程异步更新缓存。
    3. 在更新完成前,所有线程直接返回旧数据。
  • 权衡: 保证了高可用性和性能,但存在数据短暂不一致的情况。
4. 构建高可用架构
  • 哨兵或集群: 解决单机宕机引发的雪崩。
  • 多级缓存: 使用本地缓存(Caffeine/Ehcache)作为 Redis 的防御前置。

重点

  • 定义定性: 必须明确指出雪崩是"大规模 Key 失效"或"缓存集群不可用",要与穿透(数据不存在)和击穿(单点热点过期)做严格区分。
  • 核心应对策略:
    • 预案 A(预防): 强调 TTL 随机扰动 是成本最低、效果最好的普适方案。
    • 预案 B(容灾): 提到 Redis Cluster 保证高可用。
    • 预案 C(兜底): 引入 熔断限流 机制(如 Sentinel/Hystrix),当数据库压力过载时,直接拒绝部分请求,保证核心链路不死。
  • 数据一致性权衡: 提到"逻辑过期"方案体现了对 CAP 理论 中 AP(可用性)的偏向。

缓存击穿

缓存击穿是指缓存中一个热点 Key (例如秒杀商品、热搜新闻)在某个时间点过期。由于该 Key 承载了极高强度的并发访问,在缓存失效的瞬间,海量请求会同时涌向数据库,尝试重新加载数据并写回缓存,导致数据库瞬时压力激增、连接枯竭甚至崩溃。

核心特征与区别

  • 对象: 针对特定的"热点 Key"。
  • 状态: 数据库中存在该数据,但缓存已过期。
  • 区别: * 穿透: 数据库中不存在 数据。
    • 雪崩: 大规模 Key 同时失效。
    • 击穿: 单点热点 Key 失效。

解决方案

1. 互斥锁 (Mutex Lock)

这是最常用的强一致性方案。当缓存失效时,不立即去查询数据库,而是先使用 Redis 的 setnx(set if not exists)操作设置一个互斥锁。

  • 执行逻辑:
    1. 只有获得锁的线程才能查询数据库并回写缓存。
    2. 其他未获得锁的线程进行阻塞等待或重试。
  • 优点: 保证了数据的一致性,且数据库压力最小(只有一个请求到达数据库)。
  • 缺点: 吞吐量受限,在锁竞争激烈时会导致请求堆积。
2. 逻辑过期 (Logical Expiration)

该方案不设置 Redis 的物理过期时间(TTL),而是将过期时间字段存放在 Value 中。

  • 执行逻辑:
    1. 线程读取数据后,对比 Value 里的逻辑过期时间。
    2. 若过期,获取互斥锁。
    3. 获取成功后,开启新线程异步更新缓存,当前线程直接返回旧数据(陈旧数据)。
    4. 未获取到锁的线程也直接返回旧数据,不阻塞。
  • 优点: 性能极高,不会出现阻塞,保证了系统的高可用性。
  • 缺点: 无法保证数据的强一致性,在更新完成前用户会读到旧值。
相关推荐
刘~浪地球2 小时前
Redis 从入门到精通(十五):安全配置与性能优化
redis·安全·性能优化
一个有温度的技术博主11 小时前
Redis主从同步原理:从全量同步到增量同步的完整解析
redis·分布式·缓存
小红的布丁12 小时前
单线程 Redis 的高性能之道
redis·后端
Advancer-17 小时前
RedisTemplate 两种序列化实践方案
java·开发语言·redis
VelinX18 小时前
【个人学习||Redis】Redis
redis
刘~浪地球19 小时前
Redis 从入门到精通(十一):持久化配置
数据库·redis·缓存
一个有温度的技术博主21 小时前
Redis主从同步进阶:深入理解增量同步与性能优化
数据库·redis·性能优化
惺忪97981 天前
Redis安装与启动
数据库·redis·缓存
雄哥0071 天前
linux redis升级⼿册-源码部署版
linux·运维·redis