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. 未获取到锁的线程也直接返回旧数据,不阻塞。
  • 优点: 性能极高,不会出现阻塞,保证了系统的高可用性。
  • 缺点: 无法保证数据的强一致性,在更新完成前用户会读到旧值。
相关推荐
IT大白鼠2 天前
Redis 系列 · 第 01 篇——认知入门:Redis 是什么
redis·nosql
彧azz2 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
lbb 小魔仙3 天前
OpenClaw + cpolar 实战:远程 NAS、分享小游戏、RDP,再配置公网 AI 入口
数据库·人工智能·redis·oracle·prometheus
夕除3 天前
redis--018
redis
两锭子3 天前
Redis基础
redis
烟沙九洲3 天前
Redis 缓存穿透、缓存击穿、缓存雪崩
数据库·redis
字节探索3 天前
Redis 只会当缓存用?这 10 大实战场景,让你的系统快到飞起
redis·后端
于樱花森上飞舞3 天前
【Redis】哨兵详解
java·开发语言·数据库·redis
Rain的Java大神之路3 天前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
Wang's Blog3 天前
Java 接入Redis: 通用命令与键管理
java·服务器·redis