经典面试题:一文了解常见的缓存问题
在面试过程中,面试官的桌子上摆放着很多高频的面试题,能否顺利回答决定了你面试通过的概率。其中缓存问题就是其中的一份,可以说掌握缓存问题及解决方法是面试前必须准备的内容。那么缓存有什么典型的问题,出现的原因是什么,又该如何解决呢?本文,来为你一一详细介绍。
缓存问题有哪些?
缓存虽然能提升性能,但也会带来一些问题。缓存问题有很多,其中经典的缓存问题如下图所示:
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。