常见 Redis 面试题_redis面试题
1. 基础概念与数据类型
1.1 Redis 支持哪些数据类型?还有哪些“其他”数据结构?
典型问题:
Redis 数据类型有哪几种?除了常见的 String、List、Set、ZSet、Hash 之外,还有哪些数据结构?
回答思路:
-
列举五大基础类型:
-
String
-
List
-
Set
-
Hash
-
ZSet
(Sorted Set)
-
-
补充其他内置/特殊结构:
-
Bitmaps:用 String 底层位图实现,常做统计(签到、布隆过滤器)。
-
HyperLogLog:基于概率算法的基数估算,仅需 12KB 存储即能统计海量元素的基数。
-
Streams(自 Redis 5 以来):日志型、消息队列式的数据结构,支持消费者组(Consumer Group)。
-
Geospatial(地理位置):基于 Sorted Set,存储经纬度并提供距离计算、附近搜索等。
-
Pub/Sub:虽不算“存储类型”,但 Redis 提供内置发布/订阅消息机制。
-
模块扩展:如 RedisJSON、RedisGraph、RedisTimeSeries 等,如果考官提到可以说明知道 Redis 模块化生态。
-
要点提示:
-
五大类型区别:
-
String
:二进制安全,最大 512MB。 -
List
:链表或 ziplist(小规模时),双端队列,可用做消息队列。 -
Set
:无序集合,底层实现为哈希表,可做交并差等集合运算。 -
Hash
:字段-值对集合,底层是 ziplist 或 hashtable,适合存储对象。 -
ZSet
:带排序分数的集合,底层可为 skiplist+hashmap。
-
-
“其他”类型通常基于这几种底层结构衍生,面试中若问到“Redis 除了 5 种常见类型,还有哪些?”可提到 Bitmaps、HyperLogLog、Streams、Geo。
1.2 String 类型的底层数据结构
典型问题:
String 类型底层数据结构是什么?
回答思路:
-
早期(Redis < 3.2):
SDS
(Simple Dynamic String)。-
SDS
是 Redis 对 C 语言char *
的封装,记录字符串长度len
、空闲空间free
,并以\'\\0\'
结尾,避免了多次strlen
。 -
SDS
结构示例:struct sdshdr { int len; // 已用长度 int free; // 空闲剩余空间 char buf[]; // 字符数组,紧跟在结构体后};
-
优势:追加操作 O(1)(有预留空间时),二进制安全,避免缓冲区溢出。
-
-
Redis 3.2 之后:
SDS
变为多种 header 类型,根据字符串长度自动选用,例如sds16
、sds32
、sds64
等。-
小字符串(< 2^8)用 8 位
len
;更大的则用 16/32/64 位长度字段。 -
这保证了内存占用更紧凑,同时依然保留动态扩容能力。
-
要点提示:
-
面试中只要提到“SDS”,并说明它保存了
len
与free
、以\'\\0\'
结尾,就足够得分。 -
若深入追问不同版本的 header,可简单提到从
sds.h
中可以看到SDS_HDR_VAR
宏动态配置 header 长度。
2. 持久化与 AOF/RDB 区别
2.1 Redis 的持久化方式有哪些?默认使用哪种?
典型问题:
Redis 持久化方式是什么?默认的是 RDB 还是 AOF?
回答思路:
-
RDB(快照持久化)
-
定时将内存中的数据写成快照(
dump.rdb
),触发条件由save
配置决定。 -
触发时 Redis 主进程
fork
出子进程,子进程进行写盘,主进程继续提供服务。 -
优势:文件紧凑,加载速度快;劣势:可能丢失最后一次快照之后的写操作(数据丢失窗口)。
-
-
AOF(Append-Only File,追加写日志)
-
每当有写命令(
SET
、LPUSH
等)时以Redis
协议格式追加到appendonly.aof
文件。 -
同步策略由
appendfsync
决定:-
always
:每条命令写盘时fsync
(最安全,最慢)。 -
everysec
:每秒fsync
一次(折中,默认)。 -
no
:由操作系统决定何时落盘(最快,但可能丢失几秒数据)。
-
-
优势:持久性最强,可接近 0 丢失;劣势:文件膨胀大、重写过程占资源。
-
-
默认方式:
-
Redis 默认开启 RDB,AOF 默认关闭。若需要 AOF,可手动在配置文件中开启
appendonly yes
。
-
要点提示:
-
面试官如果继续追问“RDB 写盘会阻塞吗?”,答“RDB 是写在子进程,主进程继续服务,只有少许 fork 延迟”。
-
如果问“AOF 过期键是否立即写入?”,指出是写命令到 AOF,不是过期事件本身。
3. 主从复制与高可用
3.1 Redis 主从复制原理
典型问题:
Redis 集群主从复制是怎么做的?
回答思路:
-
全量复制 + 增量同步(基于 PSYNC 命令):
-
初次复制或断线重连时:
-
从节点发送
PSYNC ? -1
或者PSYNC
。 -
主节点如果无法满足增量复制条件,就触发全量复制,生成 RDB 快照并发送给从节点,从节点加载后进入同步状态。
-
-
全量复制完成后:主节点会将自己的写命令不断记录到缓冲区(replication backlog),并异步发送给从节点;从节点接收写命令并执行,实现增量同步。
-
断线恢复:如果从节点断线后重连,且主节点 backlog 里仍保留它最后的 offset,对应增量同步就可继续,否则再走全量同步。
-
-
复制流程关键点:
-
replication backlog:一个循环 buffer,保存最近几 MB 的写命令。
-
从节点与主节点维护一个
replication offset
,用于 PSYNC 判断是否能增量同步。
-
要点提示:
-
重点强调 全量 + PSYNC,以及怎样判断“增量可用/不可用”。
-
如果问到“主节点如何知道哪些写命令发给哪些从节点?”,可提“主节点为每个从节点开启一个专属输出缓存(output buffer)”,通过
replication backlog
复制。
3.2 Redis 如何保证高可用?(哨兵与集群)
典型问题:
Redis 如何保证高可用性?除了 AOF 和 RDB 之外,比如哨兵模式、集群模式是如何保证高可用的?如果集群挂了,业务如何尽快恢复?
回答思路:
-
单机 + 备份(主从复制)仅能读可用,真正故障自动化切换需要Sentinel(哨兵)。
-
哨兵模式(Sentinel):
-
Sentinel 监控:多台 Sentinel 相互感知,一旦检测到主节点下线,启动投票。
-
自动故障转移:超过半数 Sentinel 判定主不可用后,选举新主从节点中最优节点进行升级,然后通知其他从节点改为该新主。
-
客户端感知:客户端通过哨兵获取当前主节点地址,实现自动重连。
-
-
集群模式(Cluster):
-
数据分片:将 16384 个 slot 分布到各个主节点上。
-
每个主节点配 1+ 个从节点,一旦主节点故障,从节点接替;其他从节点重新分配备份。
-
节点之间 Gossip 协议:互相传播健康信息。
-
故障状态判定:当超过 50% 的主节点都认为主不可用,集群进入无法写状态;写保护机制(only-read questionable)。
-
自动切换:当一个主节点不可用时,集群在其分片的从节点集群内选举新的主。
-
-
业务恢复策略:
-
快速切换:客户端应使用 Redis 客户端库的内置 Sentinel/Cluster 支持,自动感知主变更。
-
双机房/多机房:若跨机房部署,则使用最合适的复制延迟容忍策略,或者用 Proxy+多活方案。
-
备用集群:若整个集群挂掉,可切到冷备集群或读取主库快照。
-
要点提示:
-
答案要点在于哨兵投票原理和Cluster slot 分片与自动故障转移。
-
补充“哨兵模式只是管理主从切换,集群模式则是原生分片+高可用一体化方案”。
4. 过期策略与内存淘汰
4.1 如何给 Key 设置过期时间?过期策略是怎样触发回收的?
典型问题:
Redis 是怎样设置过期时间的?如果不明确设置过期时间,key 会怎样?如果内存没满只过期了,Redis 怎么回收内存?
回答思路:
-
设置过期时间:
-
EXPIRE key seconds
:为 key 设置生存时间(秒)。 -
PEXPIRE key milliseconds
:设置毫秒级过期。 -
EXPIREAT key timestamp
/PEXPIREAT
:设置到某个时间戳过期。 -
在创建时直接设置 TTL:
SET key value EX seconds PX milliseconds NX|XX
。
-
-
过期回收方式:
-
惰性删除:当客户端访问 key 时,检查 TTL:若已过期就删除并返回不存在。
-
定期删除:每隔 100ms,随机抽样一批带 TTL 的 key(默认 20 个),检查并删除过期的。若比例过多(> 25% 过期),则缩短下一次定期删除的间隔,直到达到目标。
-
内存淘汰:只有当 Redis 内存使用接近
maxmemory
时,才根据配置的淘汰策略移除 key(即使没到 TTL,也会被删除)。
-
-
不设置过期:
-
默认 key 永不过期,除非显式调用
EXPIRE
。
-
要点提示:
-
面试时重点说出“三种回收模型”(惰性、定期、主动淘汰)以及定期删除时的动态调整策略。
-
如果考官继续追“定期删除如何避免一次性消耗过多 CPU?”,答“Redis 限制每次只随机检查 20 个 key,并在过期比率高时重复短期快速执行,保证回收效率与性能平衡”。
4.2 内存淘汰策略与 LRU 实现
典型问题:
Redis 缓存淘汰策略有哪些?LRU 算法具体怎样删除“冷数据”?
回答思路:
-
常见淘汰策略(
maxmemory-policy
):-
volatile-lru
:只对设了 TTL 的 key,用 LRU 算法淘汰。 -
volatile-lfu
:只淘汰带 TTL 的 key,用 LFU(最不常用)。 -
volatile-ttl
:优先淘汰最近要过期的 key。 -
volatile-random
:从带 TTL 的 key 随机淘汰。 -
allkeys-lru
:对所有 key,用 LRU 淘汰。 -
allkeys-lfu
:对所有 key,用 LFU 淘汰。 -
allkeys-random
:对所有 key 随机淘汰。 -
noeviction
:达到内存上限后,后续写操作返回错误。
-
-
LRU 实现细节:
-
经典 LRU 要求链表记录访问顺序,开销大。Redis 采用 近似 LRU,随机抽样 + 访问计数/更新 方式:
-
当需要淘汰时,随机从数据集中(如所有 key 或带 TTL 的 key)抽取
N
(默认为 5)个样本。 -
计算这批样本中最旧的 key,淘汰它。
-
这样既近似 LRU,又避免了链表维护的高昂开销。
-
-
LFU(Redis 4.0+)同理,用计数器加随机淘汰近似实现频率最低优先淘汰。
-
要点提示:
-
面试可重点提“Redis 的 LRU 只是近似,随机抽样+淘汰最旧的样本”。
-
如果进一步问“为何不直接用链表实现精确 LRU?”,可说“内存成本高、性能开销大,不适合高并发场景”。
5. 缓存策略与常见问题
5.1 缓存雪崩
典型问题:
如何防止 Redis 出现缓存雪崩?
回答思路:
-
缓存雪崩含义:
-
指大量 key 在同一时间过期,当后端 DB 瞬时压力增大,造成系统崩溃。
-
-
常见解决办法:
-
TTL 随机化:为各个缓存 key 设置一个随机的过期时间偏移量(如 TTL = 10min ± 0~5min),避免集中同时过期。
-
热点 key 永不过期:对于必须稳定命中的热 key,可不设置过期,或在访问时动态刷新过期。
-
对缓存失效做降级处理:当检测到后端压力高时,可临时返回旧数据、限流或熔断。
-
多级缓存/本地缓存:Redis 缓存失效时,先从本地缓存(如 Guava)取,减轻瞬时压力。
-
预热缓存:应用启动或发生批量刷新时,提前加载常用 key。
-
限流熔断:使用限流/熔断框架(如 Sentinel、Resilience4j),保护后端。
-
要点提示:
-
重点讲“随机化 TTL”“限制集中失效”和“热 key 永不过期或提前刷新的策略”。
-
如果问“缓存穿透与雪崩区别是什么?”,可快速区分:穿透是查询不存在的 key;雪崩是大量 key 同时失效。
5.2 缓存穿透
典型问题:
如何防止缓存穿透?
回答思路:
-
缓存穿透含义:
-
指客户端频繁查询不存在的 key,每次都穿过 Redis 直接打到 DB,使后端压力过大。
-
-
常见解决办法:
-
布隆过滤器(Bloom Filter):提前将所有合法 key 记录到布隆过滤器,只有通过过滤器检查的 key 才查询缓存/DB,否则直接返回空。
-
缓存空对象/Null 值:当 DB 查询到 null 时,将空值(或特殊标志)写入缓存(TTL 设短,如 60 秒),防止再次穿透。
-
校验参数合法性:在上层对 userId、产品 ID 等参数做严格校验,阻止恶意 / 异常请求。
-
请求限流:对同一黑客 IP 或相同 key 的请求限速,防止恶意爆破。
-
要点提示:
-
面试可重点强调“使用布隆过滤器” 和 “缓存空对象”的结合,使得不存在的数据不会反复打到 DB。
5.3 热点 Key(热 key)与高并发
典型问题:
Redis 热点 key 怎么解决?如何保证热点 key 的命中率?
回答思路:
-
热点 key 问题:
-
单个 key 被大量并发访问,会导致该 key 的对应命令过度落在同一个 CPU 或同一个实例上,引发单点瓶颈。
-
-
常见解决办法:
-
本地缓存 + 双写/双删:将热点数据缓存在应用进程(如 Guava、Caffeine),同时异步刷新/双删。
-
读写分离(多副本):针对热点 key,建立多个读副本(读实例或只读主从),让不同客户端读不同副本。
-
缓存分片:将单一 key 拆分为多个热点子 key,如
hotKey:1
、hotKey:2
…,取模到不同 Redis 实例。 -
使用 Redis Cluster 分片:Cluster 会根据 hash slot 将 key 分散到不同节点,天然分散并发。
-
Geo/Sorted Set 分散:若查询模式可分散,则设计索引,减少集中访问。
-
热点补偿策略:若热点 key 突然超高并发,先限流或熔断(如请求 Queue 排队、返回降级缓存)。
-
要点提示:
-
面试时最好举“电商秒杀”场景:将商品库存拆分为多份缓存,或使用“预减库存 + 异步补偿”模式,避免单点刷爆。
-
还可以补充“使用 Lua 脚本保证原子操作 + 限流”来保护热点。
6. 锁与分布式锁
6.1 Redis 实现分布式锁的常见方式
典型问题:
Redis 做分布式锁应该注意哪些?如何实现加锁、解锁,如何续期?如果锁超时了怎么办?
回答思路:
-
最简单的实现:
-
SETNX:
SET resource_name token NX PX 30000
-
NX
参数:只有当 key 不存在时才设置,保证原子性。 -
PX
设置过期时间,防止死锁。
-
-
解锁:使用 Lua 脚本检查
token
是否与自己的一致,若相同才DEL key
,以保证解锁安全性。if redis.call(\"get\", KEYS[1]) == ARGV[1] then return redis.call(\"del\", KEYS[1])else return 0end
-
-
续期:
-
如果业务执行时间超过了锁的过期时间,需要在锁快过期前,由持有锁者定时调用 Lua 脚本判断并重新设置过期时间。
-
或者使用 Redisson 等客户端库,它内置了“看门狗”机制,自动续期。
-
-
注意事项:
-
锁的唯一标识:使用 UUID 等作为 token,确保不同客户端不会误解锁。
-
过期时间设置:要大于业务执行时间,边界情况可用“看门狗”或单独线程续期。
-
解锁安全性:必须在脚本中判断 token 再删除,否则可能删了他人锁。
-
防止死锁:即使客户端崩溃,过期时间保证最终能释放锁。
-
-
Redlock 算法(选答亮点):
-
基于五台独立 Redis 实例,以多数节点加锁成功为准,保证跨机房高不可用时仍能获取到锁。
-
如果多个节点加锁耗时过长,会自动回退已加锁的节点。
-
要点提示:
-
面试可提到“单点 Redis 出问题的风险”,补充 Redlock 工作原理。
-
续期一定要说明“在业务执行前要计算剩余 TTL,多次调用脚本延长”,否则容易死锁。
7. 高并发与性能优化
7.1 Redis 是单线程还是多线程?为什么能高并发?
典型问题:
Redis 是单线程还是多线程?为什么使用单线程也能做到高并发?
回答思路:
-
核心执行模型:
-
自 Redis 采用 IO 多路复用 + 单线程事件驱动,主线程负责所有客户端连接的网络 I/O 和命令处理。
-
内存操作速度远高于磁盘 I/O,加上无锁设计,单线程执行也能达到极高的 QPS。
-
-
多线程部分(Redis 6+):
-
I/O 线程:可选开启
io-threads
,让主线程专注于命令执行,其他线程负责网络读写,缓解多 CPU 核心的网络收发瓶颈。 -
异步删除大对象:后台线程异步删除大键值对象,避免在主线程做阻塞操作。
-
AOF 重写和 RDB 子进程 本身在子进程或后台线程执行,不阻塞主线程命令处理。
-
-
高并发的基础:
-
所有数据驻留内存,对比传统数据库无磁盘随机读写开销。
-
零拷贝网络、高效的协议解析(RESP)。
-
单线程避免锁竞争、减少上下文切换。
-
要点提示:
-
面试可提及“Redis 6 中 I/O 线程只在网络收发,命令执行仍在主线程”,表现出对细节的掌握。
7.2 多路复用(IO Multiplexing)原理
典型问题:
Redis 多路复用为何可以提高查询速度?多路复用是什么原理?
回答思路:
-
多路复用模型:Redis 使用
epoll
(Linux)、kqueue
(macOS)或select
来实现 IO 多路复用。 -
工作原理:
-
主线程调用
epoll_wait
,等待多个客户端套接字 FD 上的读写事件。 -
当某个连接有数据可读时,
epoll_wait
返回,就在同一个线程中读取数据并解析命令。 -
不需要为每个连接创建一个线程,大大减少了线程切换和资源占用。
-
-
提高并发能力:
-
即使成千上万个连接处于空闲状态,
epoll
也能高效地管理,仅在真正有数据就绪时才调用回调。 -
避免了大规模线程/进程上下文切换成本。
-
要点提示:
-
面试可补充“Linux 下
epoll
支持边沿触发(edge-triggered)和水平触发(level-triggered)模式”,Redis 默认使用水平触发。
8. 哨兵模式与集群模式
8.1 哨兵模式(Sentinel)
典型问题:
Redis 哨兵模式是怎么配的?哨兵模式如何保证高可用?
回答思路:
-
配置要点示例(
sentinel.conf
):sentinel monitor mymaster 127.0.0.1 6379 2sentinel down-after-milliseconds mymaster 5000sentinel failover-timeout mymaster 10000sentinel parallel-syncs mymaster 1
-
monitor
:监控名称为mymaster
的主节点,至少 2 个 Sentinel 同意才判定主不可用。 -
down-after-milliseconds
:节点在毫秒级别内无应答,就认为下线。 -
failover-timeout
:故障转移超时时间。 -
parallel-syncs
:故障转移时并行同步的从节点个数。
-
-
故障转移流程:
-
主观下线:Sentinel 通过
PING
/PONG
检测,若 5000ms 内无响应,就标记为主观下线(Subjectively Down)。 -
客观下线:若多数 Sentinel 都认为主不可用,则标记为客观下线(Objectively Down)。
-
选举新的主节点:在从节点中选一个同步进度最新、优先级高的,从节点晋升为主。
-
重配置:其他从节点指向新主,从新主处同步数据,旧主恢复后自动成为从节点。
-
通知客户端:Sentinel 会推送变更消息给订阅的客户端(如 Lettuce、Jedis),客户端自动切换。
-
要点提示:
-
面试要点在于“Sentinel 之间通过 Gossip 协议相互通信,共同做投票选举”。
-
若问“如何避免脑裂”,可答“超过半数 Sentinel 判断才触发故障转移”;“Sentinel 本身的高可用”可用 3+ 个 Sentinel 部署。
8.2 集群模式(Cluster)
典型问题:
Redis 集群模式是如何保证高可用的?如果主库挂了,数据会同步到从库吗?集群挂了业务怎么办?
回答思路:
-
架构概览:
-
一般采用至少 6 个节点:3 主 3 从,分别负责 16384 个 slot。
-
每个主节点负责一段 slot(槽)范围,读写请求 Hash 到对应 slot。
-
每个主节点至少有一个从节点,当主节点挂掉时,从节点接管该 slot。
-
-
高可用保证:
-
复制机制:主节点将写命令同步到其从节点。
-
故障检测:Cluster 节点彼此通过 Gossip 协议检测健康状态。
-
故障转移:某主节点被多数节点判定为“下线”,集群从该主的从节点中投票选出新的主。
-
故障恢复:原主恢复后作为从节点回归并与新主同步。
-
-
业务影响 & 快速恢复:
-
读写请求路由:客户端在收到
-MOVED
重定向后,会更新本地槽位缓存并重试。 -
减少跨机房延迟:设计分片策略时可将主从分布在同一机房或不同机房,权衡网络分区影响。
-
熔断降级:业务可检测集群状态,如集群失效则回退到 DB 或二级缓存。
-
备份 & 快照:即使整个集群不可用,可用备份节点或 RDB 快照恢复。
-
要点提示:
-
面试可补充“slot rebalancing”“resharding”时如何保证数据迁移中一致性。
9. 批量操作与管道(Pipeline)
典型问题:
Redis 如何批量保存数据?批量读取数据?
MGET
与Pipeline
的区别是什么?
回答思路:
-
批量保存:
-
使用
MSET key1 val1 key2 val2 …
,但注意MSET
不是原子性的事务(其实在底层命令序列化时会一次性写入多个 key,性能较好)。 -
对于更复杂的批量逻辑,可用 Pipeline:将多条
SET
命令打包后一次性发送给 Redis,减少网络往返。
-
-
批量读取:
-
MGET key1 key2 key3 …
:一次性获取多个 key 的值。 -
Pipeline
:逐条GET
命令打包,Redis 端串行执行然后打包返回。
-
-
区别对比:
-
MGET/MSET:在服务器端一次性解析并执行多个 key 操作,速度很快,但如果需要中间对每条命令做业务判断,就不灵活。
-
Pipeline:客户端打包 N 条命令后一次性发送,服务器依次串行执行并将 N 条回复打成一个包回传。主要减少网络往返(RTT)。
-
事务(MULTI/EXEC)+ Pipeline:若需要原子性,可在 Pipeline 中加入
MULTI; …; EXEC
,确保原子执行,但会加锁。
-
要点提示:
-
面试若再追问“性能瓶颈在哪?”可答“网络延迟、序列化反序列化、服务器执行”三部分,Pipeline 主要缓解第一部分。
10.使用分布式锁注意事项
简单面试答题模板
使用分布式锁时,最关键的是保证锁的安全释放和超时释放,防止死锁和误删锁。同时需要控制锁粒度,避免影响系统吞吐。对于 Redis 分布式锁,还要考虑锁续期和锁的持有者校验。选用高可用客户端减少单点故障风险。异常路径确保释放锁,保障系统稳定运行。
附:按主题再罗列一次所有小问题及简要提示
由于问题量很大,下表仅做快速查阅,具体答案可见上文对应分类。
LTRIM
裁剪边界(O(1)),避免 DEL
全量删除导致阻塞SCAN
+ DEL
,避免 KEYS
全表扫描造成阻塞maxmemory-policy
具体淘汰策略,如 allkeys-lru/noeviction
等最后一点:
-
面试时回答要有思路:
-
先定义概念(什么是 X,X 有什么作用)。
-
然后阐述原理(原理模型、实现思路)。
-
最后给出工程实践(常见配置、常用命令、注意事项、场景案例)。
-
-
切忌空讲概念,一定要结合“Redis 在生产中怎样使用”来落地回答,才更能打动面试官。
祝你面试顺利,通过率飙升!