【Redis】一文讲透缓存更新策略与缓存预热、穿透、雪崩、击穿

Redis 缓存策略

数据变更后的处理

当 MySQL 发生新增、修改、删除时,缓存也要跟着处理。常见思路是:

  1. 先修改 MySQL
  2. 删除 Redis 中对应的 key
  3. 下次查询时再按实时生成流程写回 Redis

第二步这里选择"删除缓存",不直接修改缓存里的值,原因是缓存里的数据可能不是数据库单表字段,可能经过了关联查询、计算、排序、过滤,直接改缓存值容易漏掉逻辑。

如果倒过来,先删缓存再改 MySQL 会出现什么情况呢?单一线程下一般是没有问题的;在并发情况下,可能出现这种顺序:

  1. 线程 A 删除缓存
  2. 线程 B 查询缓存未命中,读取旧 MySQL 数据,并写入 Redis
  3. 线程 A 修改 MySQL

这导致 Redis 里留下的还是旧数据,这类问题后续处理起来会比较麻烦。

缓存更新策略

缓存更新本质上是在处理两个问题:

  • Redis 里应该提前放哪些数据
  • MySQL 数据变化后,Redis 里的旧数据怎么办

定期生成

定期生成就是按固定周期把一批数据写入 Redis,比如每隔一段时间通过 shell / python 脚本从 MySQL 查出热门数据,再写入缓存。

适合场景:

  • 数据变化频率不高
  • 查询量比较大
  • 允许短时间读到旧数据

它的好处是读请求来了以后可以直接查 Redis,请求链路比较短。问题是数据会有延迟,如果 MySQL 已经变了,但下一轮缓存生成还没执行,用户可能读到旧数据。

实时生成

实时生成也就是常见的 Cache Aside 读流程:

  1. 请求查 key,先访问 redis
  2. redis 命中则直接返回;未命中则查 MySQL
  3. MySQL 查询到数据,返回并更新 redis;未查到则直接返回

这种方式不会一开始就把所有数据都放进 Redis,而是用户访问到哪个 key,就把哪个 key 放进去。它适合数据量比较大、热点数据会自然出现的场景。经过一段时间的"动态平衡",redis 中存储的数据就逐渐成为热点数据了,这又会引出另一个问题:随着时间推移数据越写越多,逐渐达到 redis 的内存上限,旧的热点 key

访问变少了,新的热点 key 插入不进来,此时就要引进内存淘汰策略

注意事项

内存淘汰策略

如果 Redis 内存达到上限,就会触发淘汰策略(这里不一定是物理内存,还可能是 redis 本身的配置文件中规定的最大内存容量)

常见策略有以下几种👇

  • FIFO(First in first out)先进先出
    把缓存中存在时间最久的 key 给删掉
  • LRU(Least recently used)淘汰最久未使用的
    记录每个 key 的最近一次的访问时间,淘汰最近访问时间最老的 key
  • LFU(Least frequently used)淘汰访问次数最少的
    记录每个 key 最近一段时间的访问次数,淘汰访问次数最少的 key
  • Random 随机淘汰
    随机挑选一位幸运儿

缓存预热

redis 服务器首次接入的时候,服务器是没有任何数据的,所有的数据都会访问 MySQL 后再进入 redis,数据库的压力才会慢慢变小。缓存预热就是解决这种问题:它将定期生成和实时生成结合了,通过离线的方式,通过一些统计的途径,先把找到一批热点🔥数据,预先导入到 redis 中,能帮数据库承担一部分访问压力。等后续时间的推移,逐渐就使用新的热点数据来淘汰旧的热点数据

缓存穿透(penetration)

请求查询一个不存在的数据,Redis 没有,MySQL 也没有。每次请求都会打到 MySQL,缓存没有起到保护作用

一部分的原因🌧️

  • 业务设计不合理,缺少必要的参数校验
  • 开发/运维误操作将部分数据从数据库上误删
  • 恶意攻击

常见处理:

  • 缓存空值:MySQL 查不到时,把空结果也写入 Redis,并设置较短过期时间
  • 布隆过滤器:先判断 key 是否可能存在,不可能存在就直接拦截
  • 参数校验:明显非法的 id、页码、业务参数,不进入缓存和数据库查询流程

缓存空值要注意过期时间不能太长,不然真实数据后来被写入 MySQL 时,Redis 里还留着空结果,会让用户继续查不到。

缓存雪崩

在短时间内大量缓存 key 在同一时间失效,或者 Redis 整体不可用,导致缓存的命中率大幅下降,请求集中访问 MySQL,数据库的压力迅速上升,甚至直接宕机了。

出现这种情况的几个典型栗子🌰:

  • redis 突然不可用 / 集群模式下大量节点宕机
  • 某个时间点大量 key 同时过期

处理思路:

  • 给过期时间加随机值,避免大量 key 同一时间失效
  • 对特别核心的数据做缓存预热
  • 对数据库访问加限流,避免请求把 MySQL 打满

缓存雪崩是"大面积缓存同时失效"的问题。沿着这个问题再往下看,会出现一个更具体的场景:不是很多 key 同时失效,而是针对热点 key 失效,大量请求都冲向同一份数据,这就是缓存击穿。

缓存击穿(breakdown)

相当于缓存雪崩的特殊情况,针对热点 key 突然失效时,大量并发请求同时发现 Redis 未命中,大量的请求访问到 MySQL 上

它和缓存雪崩的区别:

  • 缓存雪崩:大量 key 同时失效,影响面大
  • 缓存击穿:一个热点 key 失效,但访问量非常大

常见处理:

  • 热点 key 设置更长的过期时间,或者用逻辑过期控制刷新
  • 进行服务降级,如访问数据库时使用\[分布式锁] 🔒,限制请求数据库的并发数
相关推荐
189228048612 小时前
NV041固态MT29F16T08GSLCEM9-QBES:C
人工智能·算法·microsoft·缓存·性能优化
Database_Cool_2 小时前
AnalyticDB MySQL vs Apache Doris:企业级云数仓如何选型——全维度对比指南
数据库·数据仓库·mysql·阿里云
心翼叶少2 小时前
Redis(二):设置密码
数据库·redis·缓存
_Kafka_2 小时前
Oracle平均成本计算流程
数据库·oracle
xfhuangfu2 小时前
Oracle 19c中业务表的列发生变化时使用impdp
数据库·oracle
小何code2 小时前
【Python零基础入门】第10篇:Python列表方法与应用实例
数据库·人工智能·python
Flash.kkl2 小时前
C++基于websocketpp的多用户网页五子棋项目
开发语言·网络·数据库·c++·websocket·mysql
kong@react2 小时前
milvus(向量数据库)docker容器(升级1.0)
数据库·docker·milvus
openFuyao2 小时前
在Agent时代,成本与性能权衡成为首要考量:采用以内存缓存为中心的全新拓扑架构,还是坚持以不断提升算力为核心的计算中心架构?
缓存