搜索缓存治理Redis缓存一致性治理与故障处理 - 第1节

1.1 什么是缓存

缓存(Cache)是计算机系统中用于存储临时数据副本的高速存储层。当系统请求数据时,如果缓存中存在该数据,则直接返回缓存结果,从而避免每次都从原始数据源(如数据库、磁盘或远程服务)重新获取。

在搜索场景中,缓存通常位于应用层与数据库之间,用于存储热点搜索词、搜索结果列表、用户会话等高频访问数据。

核心思想: 用空间换时间 —— 通过额外的存储空间,换取数据访问速度的数量级提升。

缓存的工作模型

下图展示了缓存介入后的标准数据读取流程:

缓存介入后的数据读取流程 应用层 缓存层 (Redis) 数据库 (MySQL) ① 查询请求 ② 缓存命中 → 直接返回 ③ 缓存未命中 → 回源 ④ 回写缓存 流程说明: 1. 应用层优先查询缓存层(Redis)。 2. 若缓存命中,直接返回数据,数据库无压力。 3. 若缓存未命中,回源数据库查询,并将结果写入缓存(回写),下次命中。

1.2 缓存的作用

缓存并非银弹,但在正确场景下能带来显著的性能收益。下表总结了缓存的核心作用及其在搜索系统中的典型体现:

作用维度 具体收益 搜索场景示例
降低延迟 内存读取 vs 磁盘读取:毫秒级 vs 十毫秒级 热门搜索词结果直接从 Redis 返回,响应时间 < 5ms
减轻数据库压力 缓存命中率 90%+ 时,数据库 QPS 降低 10 倍以上 搜索建议、联想词缓存,避免频繁查询 MySQL
提高系统吞吐量 单机 Redis 可支撑 10 万+ QPS,远超数据库 秒杀场景下的搜索结果缓存,扛住瞬时流量洪峰
平滑突发流量 缓存作为缓冲区,吸收峰值请求 突发热点事件导致搜索量暴增,缓存保护后端
实践建议: 缓存命中率是衡量缓存效果的核心指标。一般建议搜索场景的缓存命中率维持在 85% 以上,低于该阈值需要检查缓存策略或容量。

1.3 缓存穿透

定义: 查询一个根本不存在的数据。由于缓存中不存在该数据,每次请求都会穿透缓存直达数据库,导致数据库压力剧增,甚至被打垮。

产生原因

  • 恶意攻击:攻击者构造大量不存在的 Key 发起请求。
  • 业务漏洞:查询条件错误,导致 Key 在数据库中也不存在。
  • 缓存与数据库数据不一致:数据已被删除,但缓存未及时清理。

解决方案对比

方案 原理 优点 缺点
缓存空对象 查询不存在的数据时,在缓存中写入一个空值(如 null),并设置较短的 TTL 实现简单,通用性强 占用缓存空间;空值 TTL 内数据真实写入后仍返回空
布隆过滤器 在缓存前加一层布隆过滤器,判断 Key 是否存在 空间效率高,不存在误判为存在 存在误判率;不支持删除操作;实现复杂度较高
参数校验 在应用层对请求参数进行合法性校验,拦截明显无效的查询 零成本,从源头拦截 只能拦截格式错误,无法拦截合法但不存在的数据
注意: 缓存空对象时,TTL 不宜过长(建议 30-60 秒),否则可能导致数据长期不一致。布隆过滤器适合 Key 集合相对固定的场景,如用户 ID 范围已知。

1.4 缓存击穿

定义: 一个热点 Key 在缓存失效的瞬间,大量并发请求同时涌入,全部穿透到数据库,导致数据库压力瞬间飙升。

与缓存穿透的区别:缓存击穿针对的是存在但失效的热点数据,而缓存穿透针对的是根本不存在的数据。

典型场景

  • 热门商品详情页缓存过期,大量用户同时访问。
  • 热搜词缓存 TTL 到期,搜索流量集中涌入。
  • 首页推荐列表缓存失效,所有用户请求同时回源。

解决方案

方案 实现方式 适用场景
互斥锁 (Mutex) 缓存失效后,第一个请求获取锁并回源数据库,其他请求等待或重试 并发量中等,对一致性要求高
逻辑过期 缓存中存储逻辑过期时间,后台异步线程负责刷新,不真正删除缓存 允许短暂不一致,追求高可用
永不过期 + 定时刷新 热点 Key 不设置 TTL,由后台任务定期更新 数据更新频率可控,如配置类数据
推荐组合: 互斥锁 + 逻辑过期。互斥锁保证一致性,逻辑过期保证缓存不被打穿,两者结合能应对绝大多数击穿场景。

1.5 缓存雪崩

定义: 大量缓存 Key 在同一时间集中失效,或者缓存节点整体宕机,导致所有请求直接打到数据库,引发数据库崩溃甚至整个系统不可用。

缓存雪崩是三种异常中破坏力最大的,因为它涉及的是大规模的缓存失效,而非单个 Key。

常见诱因

  • 大量 Key 设置了相同的 TTL(如统一设置为 1 小时),到期时同时失效。
  • Redis 实例宕机或网络分区,导致整个缓存层不可用。
  • 缓存集群扩容/缩容时,大量 Key 被重新分布,导致缓存 Miss 率骤升。

防御策略

策略 具体做法 效果
TTL 随机化 在基础 TTL 上增加随机偏移量(如 ±20%) 避免大量 Key 同时过期
多级缓存 本地缓存(如 Caffeine)+ 分布式缓存(Redis) Redis 宕机时本地缓存仍可提供服务
缓存预热 系统启动或大促前,提前将热点数据加载到缓存 避免冷启动时的雪崩效应
限流降级 对回源数据库的请求进行限流,超出阈值直接返回默认值或错误 保护数据库不被击垮
Redis 高可用 主从 + 哨兵 / Redis Cluster 单点故障时自动切换,减少缓存层不可用时间
缓存雪崩的连锁反应与防御 正常:缓存层正常 请求 → 缓存命中 → 返回 数据库:低负载 雪崩:缓存大量失效 所有请求穿透到数据库 数据库:过载崩溃 防御措施(从源头到兜底): ① TTL 随机化 → 避免同时过期 ② 多级缓存(本地 + 分布式)→ 缓存层故障时仍有本地兜底 ③ 限流降级 + 缓存预热 → 控制回源流量,提前加载热点

1.6 三种异常对比总结

为便于记忆和面试准备,下表将三种缓存异常的核心差异进行横向对比:

异常类型 问题本质 影响范围 核心解决思路
缓存穿透 查询不存在的数据 单个 Key 或少量 Key 缓存空对象 / 布隆过滤器
缓存击穿 热点 Key 失效瞬间并发回源 单个热点 Key 互斥锁 / 逻辑过期
缓存雪崩 大量 Key 同时失效或缓存层宕机 全局 / 大规模 TTL 随机化 / 多级缓存 / 高可用
面试高频题: “请分别解释缓存穿透、击穿、雪崩的区别,并给出至少两种解决方案。” 掌握本章内容即可从容应答。