Redis常见考点
这篇适合用于互联网大厂程序员面试前突击复习。目标不是只会背名词,而是能把 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 点回答:
- 纯内存操作,避免磁盘随机 IO
- 单线程执行命令,避免了大量线程切换和锁竞争
- 基于
IO 多路复用,网络读写效率高 - 内部数据结构设计高效,比如哈希表、跳表、压缩结构等
记忆点
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 | key = user:1001 |
典型场景
- 用户资料
- 商品信息
- 配置项集合
优点
- 比把整个对象序列化成一个大字符串更灵活
- 可以按字段读写
记忆点
对象优先想到 Hash
3. List
底层是双端链表风格的逻辑结构,适合从两端插入弹出。
典型场景
- 消息队列
- 时间线
- 最新 N 条记录
常见命令
LPUSHRPUSHLPOPRPOP
注意
- 传统 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:优先淘汰更快过期的 keyvolatile-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。
常见更新策略
- 先更新缓存,再更新数据库
- 先更新数据库,再更新缓存
- 先更新数据库,再删除缓存
其中最常用的是:
先更新数据库,再删除缓存
为什么不是“更新数据库后更新缓存”
因为缓存不一定总会被读到,直接更新缓存可能白做;
而且在并发场景下,更容易出现脏数据覆盖。
为什么“删缓存”更常见
- 简单
- 懒加载思想
- 下一次读请求自然会回源重建缓存
进一步追问:删除缓存失败怎么办
- 重试
- 消息队列异步补偿
- 订阅 binlog 做最终一致性修复
记忆点
更新 DB,删除缓存,不要反过来
高可用与集群
1. 主从复制
主节点负责写,从节点负责同步数据,可以分担读压力。
流程可以这样背
- 从节点连接主节点
- 主节点把数据同步给从节点
- 后续写命令继续传播给从节点
作用
- 数据冗余
- 读写分离
- 为哨兵和故障转移提供基础
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 | user:profile:1001 |
记忆点
key 命名要能看懂、能隔离、能排查
面试高频追问清单
下面这些题,面试里非常容易连续出现:
- Redis 为什么这么快?
- Redis 到底是不是单线程?
- String、Hash、Set、ZSet 各适合什么场景?
- RDB 和 AOF 有什么区别?线上怎么选?
- 过期删除和内存淘汰有什么区别?
- LRU 和 LFU 的区别是什么?
- 什么是缓存穿透、击穿、雪崩?分别怎么解决?
- 为什么缓存一致性常用“更新数据库,再删除缓存”?
- Redis 主从、哨兵、集群分别解决什么问题?
- Redis 分布式锁为什么要用
SET NX PX? - 解锁为什么要用 Lua 脚本保证原子性?
- 什么是 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. 缓存一致性
更新数据库,再删缓存