这篇适合用于互联网大厂程序员面试前突击复习。目标不是只会背名词,而是能把 Redis 高频问题讲成一段有结构、有因果的完整回答。


目录


一页速记总表

分类 高频考点 面试常问 记忆点
基础 Redis 为什么快、线程模型 Redis 是单线程吗?为什么这么快? 纯内存 + IO 多路复用 + 高效数据结构
数据类型 String、Hash、List、Set、ZSet、Bitmap、HyperLogLog、Stream 排行榜、去重、计数分别用什么? 值用 String,对象用 Hash,队列用 List,去重用 Set,排序用 ZSet
持久化 RDB、AOF、混合持久化 RDB 和 AOF 有什么区别? RDB 快照快恢复,AOF 丢数据少
过期 惰性删除、定期删除 Redis 为什么不主动扫全量 key? 过期删除解决失效,淘汰策略解决内存满
淘汰 LRU、LFU、TTL、random allkeys-lru 和 volatile-lru 区别? volatile 只淘汰设置过期的,allkeys 淘汰全体
缓存 穿透、击穿、雪崩 怎么防止热点 key 失效把 DB 打挂? 穿透查不到,击穿一个热点过期,雪崩一批同时失效
高可用 主从复制、哨兵、Cluster Redis Cluster 为什么是 16384 槽? 主从保副本,哨兵做故障转移,集群做分片扩容
并发 分布式锁、Redisson、Lua setnx 就等于分布式锁吗? 加锁要原子,解锁要验 owner,续期防超时
线上 BigKey、HotKey、缓存一致性 双写不一致怎么处理? 先更新 DB,再删缓存

Redis 基础与线程模型

1. Redis 为什么快

Redis 快,通常从下面 4 点回答:

  1. 纯内存操作,避免磁盘随机 IO
  2. 单线程执行命令,避免了大量线程切换和锁竞争
  3. 基于 IO 多路复用,网络读写效率高
  4. 内部数据结构设计高效,比如哈希表、跳表、压缩结构等

记忆点

  • Redis 快,核心不是“单线程”,而是“内存 + 非阻塞 IO + 数据结构”
  • 单线程只是让命令执行模型简单,减少竞争,不是唯一原因

2. Redis 是单线程吗

这是高频陷阱题,推荐这样回答:

  • Redis 的命令执行核心线程通常是单线程
  • Redis 6 之后引入了多线程来处理部分网络 IO
  • 但真正执行命令、修改数据的逻辑,仍然主要是单线程串行执行

标准结论

  • 说 Redis 是单线程,不完全准确
  • 更准确的说法是:命令执行单线程,网络 IO 可多线程

3. Redis 为什么不用多线程执行命令

因为 Redis 的设计目标是:

  • 命令尽量短小
  • 在内存中快速完成
  • 避免多线程带来的锁竞争、上下文切换和实现复杂度

如果命令执行本身很快,多线程不一定更赚,反而可能更复杂。

记忆点

  • 如果瓶颈不在 CPU,而在内存和网络,多线程收益未必大

Redis 常见数据类型

1. String

最常用,底层可以存字符串、整数、浮点数。

典型场景

  • 缓存对象 JSON
  • 计数器
  • 分布式锁
  • session / token

高频追问

  • incr 为什么能做计数器?
    因为 Redis 是单线程串行执行命令,INCR 本身是原子操作。

记忆点

  • String 是万能类型,能缓存、能计数、能加锁

2. Hash

适合存对象。

例如用户信息:

1
2
key   = user:1001
field = name age city

典型场景

  • 用户资料
  • 商品信息
  • 配置项集合

优点

  • 比把整个对象序列化成一个大字符串更灵活
  • 可以按字段读写

记忆点

  • 对象优先想到 Hash

3. List

底层是双端链表风格的逻辑结构,适合从两端插入弹出。

典型场景

  • 消息队列
  • 时间线
  • 最新 N 条记录

常见命令

  • LPUSH
  • RPUSH
  • LPOP
  • RPOP

注意

  • 传统 List 可做简单队列,但复杂消息场景现在更常推荐 Stream

4. Set

无序、元素唯一。

典型场景

  • 去重
  • 共同好友
  • 标签集合
  • 抽奖名单

高频追问

  • 为什么适合做去重?
    因为 Set 天然不允许重复元素。

记忆点

  • 去重、交并差,直接想到 Set

5. ZSet

有序集合,每个元素带一个 score,底层常见理解是哈希表 + 跳表。

典型场景

  • 排行榜
  • 延时任务
  • 带权重排序

高频追问

  • 为什么排行榜常用 ZSet?
    因为它既能按成员查分数,也能按分数排序取区间。

记忆点

  • 只要有排名,就优先想到 ZSet

6. Bitmap、HyperLogLog、Stream

这几个属于面试加分项。

Bitmap

本质上是按位存储的 String。

场景

  • 用户签到
  • 活跃统计
  • 是否在线

记忆点

  • 海量布尔状态统计,用 Bitmap

HyperLogLog

用于基数统计,也就是“估算去重后的数量”。

场景

  • UV 统计
  • 独立访客数量

优点

  • 非常省内存

缺点

  • 有误差,不能拿来做精确去重明细

记忆点

  • 只关心数量,不关心明细,用 HLL

Stream

Redis 提供的消息流结构。

场景

  • 消息队列
  • 消费组
  • 可追溯消费

记忆点

  • 要消息队列能力,List 是入门,Stream 更像正式方案

持久化

1. RDB

RDB 是把某一时刻的内存数据生成快照写到磁盘。

优点

  • 文件紧凑,适合备份
  • 恢复速度快
  • 对运行时性能影响相对可控

缺点

  • 两次快照之间如果宕机,会丢失这段时间的数据

记忆点

  • RDB = 快照
  • 优点是恢复快,缺点是可能丢最后一段数据

2. AOF

AOF 会把写命令追加到日志文件中,恢复时重新执行这些命令。

appendfsync 常见有 3 种策略:

  • always:每次写命令都刷盘,最安全,性能最差
  • everysec:每秒刷盘一次,线上常用
  • no:交给操作系统决定

优点

  • 数据更安全,丢失数据更少
  • 日志可读性更强

缺点

  • 文件通常比 RDB 更大
  • 恢复速度通常比 RDB 慢

记忆点

  • AOF = 命令日志
  • 线上常见 everysec,最多丢 1 秒

3. RDB 和 AOF 的区别

对比项 RDB AOF
持久化方式 快照 命令追加
数据安全性 较弱 较强
文件体积 较小 较大
恢复速度 较快 较慢
适用场景 备份、快速恢复 更看重数据完整性

面试建议回答

  • 如果问“线上怎么选”,不要只答二选一
  • 更完整的答案是:通常会同时开启,利用 RDB 做快速恢复,AOF 提高数据安全性

4. 混合持久化

Redis 4 之后支持混合持久化,可以理解为:

  • 先用 RDB 记录一份全量快照
  • 再把后续增量命令以 AOF 方式追加

这样兼顾恢复速度和数据完整性。

记忆点

  • 混合持久化 = RDB 全量 + AOF 增量

过期删除与内存淘汰

1. 为什么需要过期机制

因为缓存天然不是永久数据,很多 key 需要自动失效,比如:

  • 验证码
  • session
  • 短期热点数据

2. 过期删除策略

Redis 不是给每个 key 单独开一个定时器,那样成本太高。

实际主要是两种方式结合:

  • 惰性删除:访问 key 时发现过期了,再删除
  • 定期删除:周期性随机抽查一部分设置过期时间的 key,删除过期项

为什么不用“定时删除”全量扫描

  • key 太多,定时扫描成本高
  • 会对 CPU 造成很大压力

记忆点

  • 惰性删除省 CPU,定期删除补漏网

3. 内存淘汰策略

过期删除解决的是“key 到时间该失效”,淘汰策略解决的是“内存已经满了怎么办”。

常见策略:

  • noeviction:不淘汰,写入报错
  • volatile-random:从设置过期时间的 key 中随机淘汰
  • volatile-ttl:优先淘汰更快过期的 key
  • volatile-lru:从设置过期时间的 key 中淘汰最近最少使用的
  • allkeys-random:从所有 key 中随机淘汰
  • allkeys-lru:从所有 key 中淘汰最近最少使用的
  • allkeys-lfu:从所有 key 中淘汰最不经常使用的

4. LRU 和 LFU 的区别

  • LRU:最近最少使用
  • LFU:最不经常使用

如果一个 key 曾经很热,但最近不常访问:

  • LRU 可能更快淘汰它
  • LFU 会综合访问频率,更适合长期热点场景

记忆点

  • LRU 看最近,LFU 看频率

5. 线上常用建议

  • 如果 Redis 纯做缓存,常见选择是 allkeys-lru
  • 如果只有部分 key 设置过期时间,可能选择 volatile-lru

缓存高频问题

这部分是大厂面试最容易连续追问的区域,建议重点背。

1. 缓存穿透

查询一个数据库和缓存里都不存在的数据,请求每次都会打到数据库。

典型场景

  • 恶意攻击,构造不存在的 id
  • 业务参数异常

解决方案

  • 缓存空对象
  • 布隆过滤器
  • 参数校验

记忆点

  • 穿透 = 查不存在的数据,缓存挡不住

2. 缓存击穿

某个热点 key 在失效瞬间,大量请求同时打到数据库。

解决方案

  • 热点 key 永不过期
  • 加互斥锁 / singleflight,只让一个线程回源
  • 提前异步刷新

记忆点

  • 击穿 = 一个热点 key 过期,瞬间打穿 DB

3. 缓存雪崩

大量 key 在同一时间集中过期,导致请求集体回源数据库。

解决方案

  • 给过期时间加随机值
  • 多级缓存
  • 限流降级
  • 预热缓存

记忆点

  • 雪崩 = 一批 key 同时挂

4. 缓存和数据库双写一致性

这是面试高频开放题,推荐优先背 Cache Aside

常见更新策略

  1. 先更新缓存,再更新数据库
  2. 先更新数据库,再更新缓存
  3. 先更新数据库,再删除缓存

其中最常用的是:

  • 先更新数据库,再删除缓存

为什么不是“更新数据库后更新缓存”

因为缓存不一定总会被读到,直接更新缓存可能白做;
而且在并发场景下,更容易出现脏数据覆盖。

为什么“删缓存”更常见

  • 简单
  • 懒加载思想
  • 下一次读请求自然会回源重建缓存

进一步追问:删除缓存失败怎么办

  • 重试
  • 消息队列异步补偿
  • 订阅 binlog 做最终一致性修复

记忆点

  • 更新 DB,删除缓存,不要反过来

高可用与集群

1. 主从复制

主节点负责写,从节点负责同步数据,可以分担读压力。

流程可以这样背

  1. 从节点连接主节点
  2. 主节点把数据同步给从节点
  3. 后续写命令继续传播给从节点

作用

  • 数据冗余
  • 读写分离
  • 为哨兵和故障转移提供基础

2. 哨兵 Sentinel

哨兵本身不存业务数据,它的核心职责是:

  • 监控主从节点是否正常
  • 主节点故障时自动选举新主
  • 通知客户端新的主节点地址

高频追问

  • 为什么哨兵一般要部署多个?
    因为需要通过多数派机制判断故障,避免误判。

记忆点

  • 哨兵不存数据,只负责监控、通知、故障转移

3. Redis Cluster

Cluster 解决的是:

  • 单机内存有限
  • 单机并发有限
  • 需要分片扩容

Redis Cluster 把数据划分到 16384 个槽位中,每个 key 会根据哈希结果落到某个槽。

面试标准回答

  • Cluster 是去中心化分片集群
  • 每个主节点负责一部分槽
  • 主节点可以挂从节点做高可用

高频追问

  • 为什么是 16384 槽?
    面试里不需要过度深挖源码,答到“槽位足够细、映射成本可控、便于集群管理”即可。

记忆点

  • Cluster = 分片 + 高可用

4. 主从、哨兵、集群的区别

方案 解决问题
主从复制 读扩展、数据副本
Sentinel 自动故障转移
Cluster 数据分片 + 扩容 + 高可用

一句话记忆

  • 主从保副本,哨兵保切换,集群保扩容

分布式锁

1. setnx 能直接当分布式锁吗

只能说是基础,不是完整答案。

更推荐的加锁方式:

1
SET lock_key unique_value NX PX 30000

含义:

  • NX:只有 key 不存在时才设置成功
  • PX:设置过期时间,避免死锁
  • unique_value:标识锁的持有者

2. 解锁为什么不能直接 delete

因为锁可能已经过期并被别人拿到,如果你直接删,可能把别人的锁删掉。

所以解锁要保证:

  • 判断锁的 value 是不是自己
  • 判断和删除必须原子执行

通常用 Lua 脚本实现。

记忆点

  • 加锁要带过期,解锁要验身份,删除要原子

3. 锁过期了业务还没执行完怎么办

解决思路:

  • 业务尽量控制执行时间
  • 看门狗续期,比如 Redisson

4. Redis 分布式锁有什么争议

如果面试官继续深挖,可以答:

  • Redis 锁适合大多数工程场景
  • 但如果要求极强一致性,通常会考虑更严格的协调组件,比如 ZooKeeper / etcd

这样回答会显得更稳健。


性能优化与线上问题

1. BigKey

BigKey 指 value 很大,或者集合里元素特别多的 key。

风险

  • 网络传输慢
  • 删除阻塞
  • 主从同步压力大

优化思路

  • 拆分 key
  • 避免单个集合过大
  • 删除时使用异步删除能力

记忆点

  • BigKey 问题本质是“单个 key 太重”

2. HotKey

某个 key 的访问量特别大,会导致单点压力集中。

解决方案

  • 本地缓存
  • 多副本分担读
  • 热点探测与预热
  • 对热点 key 做分片

记忆点

  • HotKey 问题本质是“单个 key 太热”

3. Redis 常见阻塞原因

  • 执行了耗时命令
  • 一次性返回大量数据
  • 删除超大 key
  • 持久化压力过高

常见危险命令

  • KEYS *
  • FLUSHALL
  • 大范围 HGETALL
  • 大范围 SMEMBERS

记忆点

  • 线上少用全量扫描和大结果集命令

4. 如何设计 Redis Key

建议:

  • 语义清晰
  • 分层命名
  • 带业务前缀
  • 控制长度

例如:

1
2
3
user:profile:1001
order:detail:20260324:9001
feed:timeline:uid:1001

记忆点

  • key 命名要能看懂、能隔离、能排查

面试高频追问清单

下面这些题,面试里非常容易连续出现:

  1. Redis 为什么这么快?
  2. Redis 到底是不是单线程?
  3. String、Hash、Set、ZSet 各适合什么场景?
  4. RDB 和 AOF 有什么区别?线上怎么选?
  5. 过期删除和内存淘汰有什么区别?
  6. LRU 和 LFU 的区别是什么?
  7. 什么是缓存穿透、击穿、雪崩?分别怎么解决?
  8. 为什么缓存一致性常用“更新数据库,再删除缓存”?
  9. Redis 主从、哨兵、集群分别解决什么问题?
  10. Redis 分布式锁为什么要用 SET NX PX
  11. 解锁为什么要用 Lua 脚本保证原子性?
  12. 什么是 BigKey、HotKey,线上怎么治理?

背诵口诀

1. Redis 核心能力

  • Redis 快:内存、单线程、IO 复用、结构高效

2. 数据类型

  • 值用 String,对象用 Hash,队列用 List,去重用 Set,排行用 ZSet

3. 持久化

  • RDB 快照恢复快,AOF 日志更安全
  • 混合持久化 = RDB 全量 + AOF 增量

4. 过期与淘汰

  • 过期删除管失效,淘汰策略管内存满
  • LRU 看最近,LFU 看频率

5. 缓存异常

  • 穿透查不到,击穿一个热点过期,雪崩一批同时失效

6. 高可用

  • 主从保副本,哨兵保切换,集群保扩容

7. 分布式锁

  • 加锁带过期,解锁验 owner,删除要原子

8. 缓存一致性

  • 更新数据库,再删缓存