Redis面试题 03
这份清单涵盖了 Redis 在生产环境中最核心的实战问题,包括数据分布、内存管理、高并发场景下的缓存异常(穿透/击穿/雪崩)以及一致性保障。这些都是中高级开发岗位面试的“必考题”。
以下是针对这 10 个问题的高分回答话术整理,按逻辑模块分类,方便记忆和表达。
第一部分:核心机制与算法 (Q1-Q3)
1. 哈希分区算法有哪些?
核心回答:
"Redis Cluster 和分布式缓存系统中常用的哈希分区算法主要有三种:
- 取模哈希 (
hash(key) % N):
- 原理:根据节点数量 N 取模。
- 缺点:扩容/缩容困难。当节点数 N 变化时,大部分 Key 的映射位置都会改变,导致大量缓存失效和数据库压力激增(缓存雪崩)。
- 一致性哈希 (Consistent Hashing):
- 原理:将节点和数据都映射到一个环形空间上,数据顺时针找到的第一个节点即为归属节点。
- 优点:扩容/缩容时,只影响相邻节点的数据,迁移量小。
- 缺点:容易产生数据倾斜(热点节点),通常通过引入虚拟节点来解决。
- 哈希槽 (Hash Slots) —— Redis Cluster 采用方案:
- 原理:预设16384 个槽,Key 通过
CRC16(key) % 16384计算归属槽,每个节点负责一部分槽。- 优点:解耦了节点数量和槽数量,扩容缩容只需迁移槽,非常灵活且均衡。”
2. 过期键的删除策略?
核心回答:
"Redis 采用的是惰性删除 + 定期删除相结合的策略,以平衡 CPU 和内存开销:
- 惰性删除:客户端访问某个 Key 时,检查是否过期。如果过期,立即删除并返回空。
- 优点:最大限度节省 CPU。缺点:如果大量 Key 过期但未被访问,会占用内存。
- 定期删除:Redis 默认每秒执行 10 次过期扫描,随机抽取一部分设置了过期时间的 Key 进行检查和删除。
- 策略:如果过期 Key 比例高,就多做一点;如果比例低,就少做一点,避免长时间占用 CPU。
- 总结:两者结合既保证了内存不会无限增长,又避免了因集中删除导致的线程阻塞。”
3. 内存淘汰策略有哪些?
核心回答:
“当 Redis 内存达到maxmemory限制时,会根据配置的maxmemory-policy进行淘汰,主要有 6 种策略:
- noeviction(默认):不淘汰,直接报错(写操作失败)。
- allkeys-lru:从所有 Key 中,淘汰最近最少使用的 Key。(最常用,适合纯缓存场景)
- volatile-lru:仅从设置了过期时间的 Key 中,淘汰最近最少使用的。
- allkeys-random:从所有 Key 中随机淘汰。
- volatile-random:仅从设置过期时间的 Key 中随机淘汰。
- volatile-ttl:仅从设置过期时间的 Key 中,淘汰剩余存活时间更短的 Key。
- 建议:一般业务推荐使用
allkeys-lru,让 Redis 自动管理热点数据。”
第二部分:缓存一致性 (Q4)
4. 如何保证缓存与数据库双写时的数据一致性?
核心回答:
“这是一个经典的最终一致性问题,没有完美方案,只有权衡:
- 首选方案:先更新数据库,再删除缓存(Cache Aside Pattern)。
- 理由:删除比更新成本低,且能避免并发写导致的脏数据。即使删缓存失败,下次读取时会触发‘读缺失’,重新加载最新数据。
- 解决删除失败:引入重试机制或异步保障。
- 简单版:代码捕获异常,重试删除几次。
- 进阶版(推荐):利用Canal监听 MySQL Binlog,将变更消息发送到MQ,消费者消费消息后删除 Redis。利用 MQ 的重试和死信队列保证删除操作最终成功。
- 特殊场景:如果对一致性要求极高(如金融),可考虑延时双删(先删->更 DB->休眠->再删),但会增加接口耗时,现在用得较少。
- 结论:绝大多数业务接受秒级不一致,采用‘更 DB 删 Cache + Canal 异步兜底’是最佳实践。”
第三部分:缓存三大异常与解决方案 (Q5-Q9)
5. 说一说缓存穿透
核心回答:
"现象:查询一个根本不存在的数据,缓存没命中,请求直达数据库,数据库也没数据。恶意攻击者利用此点频繁请求不存在的 Key,打挂数据库。
解决方案:
- 缓存空对象:即使数据库查不到,也将
null值写入缓存并设置较短过期时间(如 5 分钟)。- 布隆过滤器 (Bloom Filter):在缓存前加一层过滤器,快速判断 Key 是否存在。如果过滤器说没有,直接拦截,不查库。(最优解,但有误判率)
- 参数校验:在接口层对 ID 等参数做基本合法性校验(如 ID 不能为负数)。”
6. 说一说缓存雪崩
核心回答:
"现象:大量 Key 在同一时间过期,或者 Redis 服务宕机,导致瞬间所有请求涌向数据库,造成数据库崩溃。
解决方案:
- 随机过期时间:给 Key 的过期时间加上一个随机值(如
expire = base + random(1, 5)),避免集体失效。- 高可用架构:搭建 Redis 哨兵或集群,防止单点宕机。
- 限流降级:当检测到流量激增时,对非核心业务进行限流或服务降级,保护数据库。
- 预热:提前加载热点数据到缓存。”
7. 缓存击穿
核心回答:
"现象:某个热点 Key(如秒杀商品)突然过期,此时有大量并发请求同时访问该 Key,全部穿透到数据库。
解决方案:
- 互斥锁 (Mutex Lock):发现缓存失效后,不是直接查库,而是先获取分布式锁。只有拿到锁的线程去查库并回写缓存,其他线程等待或重试。
- 逻辑过期:不设置物理过期时间,而是在 Value 里存一个
expireTime字段。读取时发现逻辑过期,开启一个异步线程去更新缓存,当前线程直接返回旧值(保证可用性,牺牲短暂一致性)。”
8. 缓存预热
核心回答:
"定义:系统上线前或大促前,主动将热点数据加载到缓存中,避免冷启动时用户请求直接打到数据库。
实施方式:
- 脚本定时任务:编写脚本在低峰期全量或增量加载数据。
- 启动时加载:应用启动时同步加载(注意控制数据量,避免启动过慢)。
- 离线计算推送:通过大数据平台计算出热点 Key,推送到 Redis。”
9. 缓存降级
核心回答:
"定义:当缓存服务不可用(如 Redis 挂了)或负载过高时,为了保住核心业务,暂时停用缓存,直接访问数据库,或者返回默认值/兜底数据。
实施要点:
- 开关控制:通过配置中心(如 Nacos/Apollo)动态控制是否走缓存。
- 熔断机制:结合 Sentinel/Hystrix,当缓存错误率达到阈值自动熔断。
- 有损服务:降级后可能响应变慢或数据稍旧,需提前告知用户或限制功能。”
第四部分:运维与优化 (Q10)
10. Redis 大 Key 怎么处理?
核心回答:
"危害:大 Key(如几 MB 的 String 或几十万元素的 Hash/List)会导致网络阻塞、内存不均、主从同步延迟,甚至引发 OOM。
发现方法:
- 命令行:使用
redis-cli --bigkeys分析。- 监控:通过 RDB 文件分析工具(如 rdb-tools)或线上监控告警。
处理方案:
- 拆分:将大 Hash 拆分为
hash_0,hash_1...;大 List 拆分为多个小 List。- 压缩:对 String 类型的大 Value 进行序列化压缩(如 GZIP)。
- 清理:对于不再需要的大 Key,不要直接用
DEL(会阻塞主线程),要用UNLINK(异步非阻塞删除)。”
💡 面试加分技巧:场景化串联
如果面试官问得比较散,你可以尝试串联起来回答,展示系统性思维。例如:
“关于缓存的高可用设计,我们首先要考虑数据分布(Q1),Redis Cluster 的哈希槽机制很好地解决了扩容问题。
接着是内存管理(Q2, Q3),通过惰性+定期删除和 LRU 淘汰策略,保证内存不被撑爆。
在生产中,最怕遇到三大异常(Q5-Q7):穿透用布隆过滤器,雪崩靠随机过期,击穿过互斥锁。
同时,我们要通过预热(Q8)和降级(Q9)预案来应对突发流量。
最后,对于大 Key(Q10)要提前发现和拆分,避免单点故障。
整个过程中,数据一致性(Q4)是我们始终关注的重点,通常采用‘更 DB 删 Cache + 异步兜底’的最终一致性方案。”
这样回答会让面试官觉得你不仅背了知识点,而且真正理解了整个技术体系。
