经典面试题:一文了解常见的缓存问题

在面试过程中,面试官的桌子上摆放着很多高频的面试题,能否顺利回答决定了你面试通过的概率。其中缓存问题就是其中的一份,可以说掌握缓存问题及解决方法是面试前必须准备的内容。那么缓存有什么典型的问题,出现的原因是什么,又该如何解决呢?本文,来为你一一详细介绍。

缓存问题有哪些?

缓存虽然能提升性能,但也会带来一些问题。缓存问题有很多,其中经典的缓存问题如下图所示:

1. 缓存雪崩

问题描述

缓存服务宕机,导致所有请求直接访问数据库,引发数据库压力激增甚至崩溃。

解决方法

  • 集群化部署缓存(如Redis Cluster),实现高可用;
  • 使用熔断降级机制,限制数据库访问量;
  • 读写分离;
  • 使用本地缓存;
  • 对缓存体系进行实时监控, 当请求访问的慢速比超过阀值时,及时报警,通过机器替换、服务替换进行及时恢复;也可以通过各种自动故障转移策略,自动关闭异常接口、停止边缘服务、停止部分非核心功能措施,确保在极端场景下,核心功能的正常运行。

2. 缓存失效

问题描述

大量缓存数据同时过期,导致所有请求直接访问数据库,引发数据库压力激增甚至崩溃。

解决方法

  • 设置随机过期时间,避免同时失效。使用公式:过期时间 = base 时间 + 随机时间

3. 缓存穿透

问题描述

频繁查询不存在的数据(如恶意攻击),缓存和数据库均无法命中,导致无效请求穿透到数据库。

解决方法

  • 布隆过滤器(Bloom Filter): 构建一个 BloomFilter 缓存过滤器,记录全量数据,这样访问数据时,可以直接通过 BloomFilter 判断这个 key 是否存在,如果不存在直接返回即可,根本无需查缓存和 DB。但是BloomFilter 要缓存全量的 key,这就要求全量的 key 数量不大,10亿条数据以内最佳,因为 10亿 条数据大概要占用 1.2GB 的内存。也可以用 BloomFilter 缓存非法 key,每次发现一个 key 是不存在的非法 key,就记录到 BloomFilter 中,这种记录方案,会导致 BloomFilter 存储的 key 持续高速增长,为了避免记录 key 太多而导致误判率增大,需要定期清零处理;
  • 缓存空值(Null Object): 为不存在的 Key 设置短时间缓存,避免重复查询数据库。

4. 缓存击穿

问题描述

某个热点Key突然过期,大量并发请求直接访问数据库,导致瞬时压力过大。

解决方法

  • 永久缓存: 针对基本不会发生更新的场景,可以把 key 设置为永不过期,让 key 常驻缓存;
    **定期缓存:针对需要频繁更新的场景,**可以使用额外的补偿程序来定时刷新缓存或者延长 key 的实效时间;
  • 分布式锁: 针对偶尔需要更新的场景,可以对请求代码使用分布式互斥锁,使得少部分直接请求数据库后更新缓存,而剩余的其他请求直接使用新缓存即可,或者采用本地互斥锁保证仅有少量请求能够更新缓存,其余请求访问新缓存。

5. 缓存与数据库一致性

问题描述

缓存与数据库数据不一致,常见于更新操作时,比如更新 DB 后,写缓存失败,从而导致缓存中存的是老数据。

解决方式

  • 删除 Key: 写入/更新的时候,先删除缓存中的 Key,再更新数据库;
  • 订阅数据库Binlog: 通过监听数据库变更同步更新缓存(如Canal工具);
  • 最终一致性容忍: 根据业务场景接受短暂不一致。

6. 缓存预热

问题描述

系统启动时缓存为空,大量请求直接访问数据库导致冷启动压力。

解决方式

  • 提前加载热点数据到缓存(如统计分析高频访问的Key)。

7. 缓存淘汰策略

问题描述

缓存空间有限时,如何选择淘汰哪些数据以腾出空间。

解决方式

  • LRU(Least Recently Used): 淘汰最近最少使用的数据;
  • LFU(Least Frequently Used): 淘汰访问频率最低的数据;
  • TTL(Time To Live):基于过期时间淘汰。

8. 缓存污染

问题描述

缓存中存储了低频访问的数据,挤占了热点数据的空间。

解决方式

  • 优化缓存淘汰策略(如结合LRU和LFU);
  • 定期清理非热点数据。

9. 热点 Key

问题描述

某些业务在某一瞬间或某一时间段内可能会成为热点业务,热点业务的数据可能会产生热点key,比如微博上热榜数据。

解决方式

  • 找到对应的热点 key,将这些热 key 进行分散处理,比如一个热 key 名字叫 hotkey,可以被分散为 hotkey#1、hotkey#2、hotkey#3,......hotkey#n,这 n 个 key 分散存在多个缓存节点,然后 client 端请求时,随机访问其中某个后缀的 hotkey,这样就可以把热 key 的请求打散,避免一个缓存节点过载;
  • 也可以 key 的名字不变,对缓存提前进行多副本+多级结合的缓存架构设计。再次,如果热 key 较多,还可以通过监控体系对缓存的 SLA 实时监控,通过快速扩容来减少热 key 的冲击。最后,业务端还可以使用本地缓存,将这些热 key 记录在本地缓存,来减少对远程缓存的冲击。

10. 大 Key

问题描述

缓存中某些 key 的 value 的值过大,导致写操作超时、加载速度缓慢等问题。

解决方式

  • 如果数据存在 MC 中,可以设计一个缓存阀值,当 value 的长度超过阀值,则对内容启用压缩,让 KV 尽量保持小的 size,其次评估大 key 所占的比例,在 Mc 启动之初,就立即预写足够数据的大 key,让 MC 预先分配足够多的 trunk size 较大的 slab。确保后面系统运行时,大 key 有足够的空间来进行缓存;
  • 如果数据存在 Redis 中,比如业务数据存 set 格式,大 key 对应的 set 结构有几千几万个元素,这种写入 Redis 时会消耗很长的时间,导致 Redis 卡顿。此时,可以扩展新的数据结构,同时让 client 在这些大 key 写缓存之前,进行序列化构建,然后通过 restore 一次性写入;
  • 将大 key 分拆为多个 key,尽量减少大 key 的存在。同时由于大 key 一旦穿透到 DB,加载耗时很大,所以可以对这些大 key 进行特殊照顾,比如设置较长的过期时间,比如缓存内部在淘汰 key 时,同等条件下,尽量不淘汰这些大 key。
相关推荐
Full Stack Developme几秒前
Redis 可以实现哪些业务功能
数据库·redis·缓存
努力学算法的蒟蒻7 分钟前
day58(1.9)——leetcode面试经典150
算法·leetcode·面试
VermiliEiz25 分钟前
二进制文件部署k8s方式(5)
云原生·容器·kubernetes
西京刀客1 小时前
golang路由与框架选型(对比原生net/http、httprouter、Gin)
http·golang·gin
UrbanJazzerati1 小时前
统计学的"测谎仪":一文搞懂方差、标准差与“N-1”的秘密
面试
Mr -老鬼1 小时前
Rust与Go:从学习到实战的全方位对比
学习·golang·rust
2301_810746311 小时前
CKA冲刺40天笔记 - day24 Kubernetes Clusterrole 和 Clusterrole Binding
笔记·容器·kubernetes
顾林海1 小时前
Android文件系统安全与权限控制:给应用数据上把“安全锁”
android·面试·操作系统
青莲8431 小时前
Android 动画机制完整详解
android·前端·面试
想摆烂的不会研究的研究生2 小时前
每日八股——Redis(2)
数据库·redis·缓存