缓存穿透怎么解决?

wen python案例 2

本文目录导读:

缓存穿透怎么解决?

  1. 缓存穿透的四大解决方案
  2. 方案对比与选择
  3. 面试回答建议

这是一个非常经典的面试题,也是高并发系统设计中必须考虑的问题。

核心理解:

  • 缓存穿透:指查询一个根本不存在的数据,由于缓存中不命中,请求直接打到数据库,而数据库里也没有这条记录,导致每次查询都穿透缓存直达数据库。
  • 后果:如果有人恶意攻击,大量请求“不存在的数据”(比如不断请求 user_id=-1 或随机ID),数据库会因承受巨大压力而崩溃。

缓存穿透的四大解决方案

以下是业界最常用的解决方案,按推荐程度和适用场景排序。

缓存空对象(最常见、最直接)

这是最基础也最容易实现的方法。

  • 做法:当数据库查询结果为空时,仍然将这个“空结果”(如 null、、特殊标记)缓存起来,并设置一个较短的过期时间(30-60 秒)。
  • 优点:实现简单,能有效防止大量相同“不存在 key”的请求穿透。
  • 缺点
    • 需要存储大量空值,浪费缓存空间。
    • 如果过期时间设置不当,可能导致一段时间内数据和数据库不一致(比如数据刚被写入数据库,但缓存中的空对象还没过期)。

代码示例(伪代码):

public User getUserById(String userId) {
    // 1. 从缓存获取
    User user = cache.get(userId);
    if (user != null) {
        return user;
    }
    // 2. 缓存未命中,查数据库
    user = dao.getUserById(userId);
    // 3. 关键步骤:无论数据库有没有,都写入缓存
    if (user == null) {
        // 缓存空对象,设置短过期时间(例如30秒)
        cache.set(userId, new User(), 30); 
    } else {
        cache.set(userId, user, 3600); // 正常数据缓存1小时
    }
    return user;
}

布隆过滤器(最优雅、最彻底)

在请求到达缓存层之前,先用布隆过滤器判断 key 是否可能存在。

  • 做法:将数据库中所有存在的 key(如用户ID)预加载到布隆过滤器中,当一个请求来时,先检查过滤器。
    • 如果布隆过滤器说“不存在”,则直接返回“数据不存在”,根本不去查询缓存或数据库
    • 如果布隆过滤器说“存在”,再去查询缓存或数据库。
  • 优点:内存占用极低(几百万数据只占几十MB),查询速度极快(O(1)),能100%拦截不存在的 key 对后端的冲击。
  • 缺点
    • 存在误判率(布隆过滤器说“存在”时可能不存在,但说“不存在”时一定不存在),业务上可以接受少量误判。
    • 数据更新时需要同步更新布隆过滤器,维护成本略高(可以使用布谷鸟过滤器或分段布隆过滤器来解决)。
  • 典型场景:新闻推荐、用户ID校验、防止重复爬虫。

接口校验(前端防御)

将非法请求拦截在业务逻辑之前。

  • 做法
    • 对用户输入进行严格的合法性校验(如 ID 格式、长度、是否为负数等)。
    • 对于敏感数据(如ID为0或负数),直接返回错误,不继续查询。
  • 场景:简单有效,但无法防御所有攻击(比如请求一个根本不存在的合法格式的ID,如 user_id=999999)。

热点参数限流/降级

在高并发场景下作为兜底方案。

  • 做法:监控数据库查询频率,如果发现某个 key 的数据库QPS(每秒查询率)异常升高,对该 key 的请求进行限流熔断,返回默认值或降级信息。
  • 场景:防止恶意攻击打垮数据库,但属于“治标不治本”,一般配合上述方案使用。

方案对比与选择

方案 适用场景 复杂度 内存占用 防御效果 主要缺点
缓存空对象 通用,小规模系统、数据量不大 高(存很多空key) 拦截重复穿透 浪费内存,有短暂不一致
布隆过滤器 大规模系统、高并发、数据量级大 极低 彻底拦截(零漏网) 有误判率,需数据同步
接口校验 任何系统,作为辅助 拦截明显恶意请求 无法防御合法格式的不存在ID
限流/降级 兜底方案,保护数据库 被动防御 影响正常用户

面试回答建议

当面试官问这个问题时,建议按以下逻辑回答,展现你的思考深度:

  1. 先定义问题:“缓存穿透指的是查询一个不存在的数据,导致请求绕过缓存直接打到数据库,如果并发高,数据库可能瞬间被打爆。”
  2. 提出解决方案,按推荐顺序:
    • “最直接的办法是缓存空对象,即使数据库没有,也缓存一个空结果并设置短过期时间,这能解决大部分问题,但会消耗缓存空间。”
    • “更优的方案是使用布隆过滤器,在应用启动时,将数据库所有主键加载到布隆过滤器中,请求到达时先判断 key 是否在过滤器中,如果不存在,直接拒绝,这种方法内存占用极低,能从根本上拦截穿透。”
    • 接口层做参数校验(比如ID格式、范围)是必须的,可以在布隆过滤器之前再拦截一层恶意攻击。”
  3. 结合业务场景说明:“如果业务数据量巨大(比如上亿用户ID),我会优先选择布隆过滤器;如果业务数据量较小或者团队想快速上线,可以先采用缓存空对象,后续再优化为布隆过滤器。”

这样的回答既能体现你对技术原理的掌握,也能展示你根据场景权衡利弊的工程思维。

抱歉,评论功能暂时关闭!