Administrator 11 阅读 面试

Redis 高频面试题(含口述版回答)


1. 什么是 Redis?为什么要用 Redis?

口述版:

Redis 是一个基于内存的高性能 Key-Value 数据库,支持多种数据结构,读写速度非常快,常被用作缓存、分布式锁、消息队列、排行榜等场景。

我们用它主要有几个原因:

  1. 性能高,数据在内存,读写延迟低。
  2. 支持丰富的数据类型,不只是简单的字符串。
  3. 支持持久化,重启后数据可以恢复。
  4. 支持主从、哨兵、集群,能做高可用和水平扩展。

简单说,就是用空间换时间,把热点数据放到内存,减轻数据库压力,提升系统响应速度。


2. Redis 有哪些数据类型?分别用在什么场景?

口述版:

常用的有五种基础类型,加上一些进阶结构:

类型 典型场景
String 缓存对象、计数器、分布式锁、验证码
Hash 缓存用户信息、商品详情(一个对象多个字段)
List 消息队列、最新动态、时间线
Set 去重、共同好友、标签、抽奖
ZSet(有序集合) 排行榜、延时队列、优先级队列

进阶的还有 Bitmap(签到、活跃用户)、HyperLogLog(UV 统计)、Geo(附近的人)、Stream(消息队列)。

面试时重点说清每种类型适合什么业务,比只背名字更重要。


3. Redis 为什么这么快?

口述版:

主要有几点:

  1. 数据在内存,内存操作比磁盘快几个数量级。
  2. 单线程模型(执行命令的线程是单线程),避免了线程切换和竞态锁的开销。
  3. 高效的数据结构,底层用了 SDS、字典、跳表、压缩列表等针对场景优化的结构。
  4. I/O 多路复用,一个线程可以处理大量连接。
  5. 操作简单,大多数命令是 O(1) 或 O(log N)。

注意:Redis 6 之后有了多线程,但主要是用在网络 I/O 上,真正执行命令的还是单线程,保证了操作的原子性。


4. Redis 持久化方式有哪些?区别是什么?

口述版:

主要有两种:RDB 和 AOF,也可以混合使用。

RDB(快照):

  • 在某个时间点把内存数据生成一个压缩的二进制快照文件。
  • 优点:文件小,恢复快,适合备份。
  • 缺点:可能丢失最后一次快照之后的数据;数据量大时 fork 会卡顿。

AOF(追加日志):

  • 把每一条写命令追加到日志文件。
  • 优点:数据更完整,丢失少(可配置 everysec)。
  • 缺点:文件大,恢复慢;需要定期重写(rewrite)。

混合持久化(Redis 4.0+):

  • AOF 重写时把当时的数据以 RDB 格式写在前面,后面再追加增量 AOF 命令。兼顾恢复速度和数据完整性。

生产中一般会同时开 RDB + AOF,或者直接用混合持久化。


5. Redis 过期删除策略是什么?

口述版:

Redis 对过期 key 采用「惰性删除 + 定期删除」组合:

  1. 惰性删除:访问一个 key 时,发现它过期了,就立刻删掉。优点是省 CPU,缺点是过期 key 如果一直不被访问,会一直占着内存。
  2. 定期删除:Redis 会定期随机抽一批设置了过期时间的 key 检查,过期的就删掉。通过限制每次检查的时间和数量,避免阻塞。

两者结合,在内存占用和 CPU 之间做平衡。


6. Redis 内存淘汰策略有哪些?

口述版:

当内存达到 maxmemory 上限时,会触发淘汰。常见策略:

  • noeviction:不淘汰,写操作直接报错(默认)。
  • allkeys-lru:在所有 key 里淘汰最近最少使用的。
  • volatile-lru:只在设置了过期时间的 key 里做 LRU。
  • allkeys-lfu:淘汰访问频率最低的(Redis 4.0+)。
  • volatile-lfu:只在过期 key 里做 LFU。
  • allkeys-random / volatile-random:随机淘汰。
  • volatile-ttl:优先淘汰剩余生存时间最短的。

缓存场景一般用 allkeys-lru 或 allkeys-lfu。


7. 缓存穿透、缓存击穿、缓存雪崩的区别和解决方案?

口述版:

这是必考题,一定要分清楚:

问题 含义 常见解决办法
缓存穿透 查一个一定不存在的数据,缓存没有,每次都打到 DB 1. 缓存空值(短过期时间)
2. 布隆过滤器拦截
缓存击穿 一个热点 key 过期的瞬间,大量请求打到 DB 1. 互斥锁(只让一个线程去查 DB 重建缓存)
2. 热点数据永不过期 + 异步更新
缓存雪崩 大量 key 同时过期,或 Redis 宕机,请求全部打到 DB 1. 过期时间加随机值,避免同时失效
2. Redis 高可用(哨兵/集群)
3. 限流降级
4. 多级缓存

一句话区分:

  • 穿透:查不存在的数据
  • 击穿:热点 key 失效
  • 雪崩:大面积失效或 Redis 挂了

8. 如何实现 Redis 分布式锁?

口述版:

最基础的版本是用 SET key value NX EX seconds:

  • NX:只有 key 不存在才设置成功(保证互斥)
  • EX:设置过期时间,防止死锁

释放锁时要判断 value 是不是自己的(防止误删别人的锁),一般用 Lua 脚本保证原子性:

if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end

进阶问题:

  • 锁续期:业务没执行完锁就过期了 → 用 Redisson 的看门狗机制自动续期。
  • 可重入:同一线程多次加锁 → Redisson 支持。
  • 主从切换丢锁:在 Redis 集群/哨兵下,推荐用 Redisson 的红锁(RedLock)或接受极低概率问题,业务做好幂等。

生产中更推荐直接用 Redisson,而不是自己造轮子。


9. Redis 单线程为什么还能支持高并发?

口述版:

这里说的单线程,指的是执行命令的线程是单线程,不是整个 Redis 只有一个线程。

原因:

  1. 纯内存操作,本身就很快。
  2. 单线程避免了线程切换和锁竞争。
  3. 使用 I/O 多路复用(epoll),一个线程可以同时处理大量连接的读写事件。
  4. 大多数命令时间复杂度很低。

所以单线程反而成为优势:简单、原子、足够快。瓶颈通常在内存、网络或大数据结构操作,而不是 CPU。


10. Redis 主从、哨兵、集群的区别?

口述版:

  • 主从复制:一主多从,读写分离,数据备份。缺点是主挂了需要人工切换。
  • 哨兵(Sentinel):在主从基础上增加监控和自动故障转移。主挂了,哨兵会选新主并通知客户端。解决了高可用,但仍是全量数据,受单机内存限制。
  • 集群(Cluster):数据分片存储,每个节点存一部分数据(16384 个槽)。支持水平扩展,也能自动故障转移。适合数据量大、需要高并发写的场景。

简单选型:

  • 数据量不大、要高可用 → 哨兵
  • 数据量大、要水平扩展 → 集群

11. 什么是大 Key?有什么危害?怎么处理?

口述版:

大 Key 一般指:

  • String 超过 10 KB
  • Hash/List/Set/ZSet 元素个数过多(比如上万),或总大小很大

危害:

  1. 内存占用不均,容易触发淘汰或 OOM。
  2. 操作耗时变长,阻塞其他命令(单线程)。
  3. 主从同步、RBD/AOF 重写变慢。
  4. 集群迁移槽时卡住。

处理思路:

  • 拆分大 Key(按业务拆成多个小 Key)
  • 对大集合做分片存储
  • 定期清理、压缩
  • 用 UNLINK(异步删除)代替 DEL
  • 监控并扫描大 Key(redis-cli --bigkeys 或自行扫描)

12. 缓存和数据库一致性怎么保证?

口述版:

这是经典难题,没有完美方案,只有适合的权衡。

常见做法:

  1. Cache Aside(旁路缓存)(最常用):

    • 读:先读缓存,没有再读 DB,回写缓存
    • 写:先更新 DB,再删除缓存(而不是更新缓存)
  2. 删除缓存失败怎么办?

    • 重试
    • 用消息队列保证最终删除
    • 设置合理的过期时间作为兜底
  3. 更强一致性需求可以用:

    • 延迟双删
    • 订阅 binlog 异步删缓存(Canal 等)
    • 分布式事务(成本高,一般不这么做)

实际项目中,大多数业务接受最终一致性,用「更新 DB + 删除缓存 + 过期时间」就够了。


13. Pipeline 和事务的区别?

口述版:

  • Pipeline:把多个命令打包一次发给 Redis,减少网络往返(RTT)。不保证原子性,只是提升性能。
  • 事务(MULTI/EXEC):命令队列化,然后一次执行,保证这些命令按顺序执行,中间不会插入别人的命令。但不像数据库事务那样支持回滚,某条命令失败,其他命令照样执行。

需要原子性又要多命令时,更推荐用 Lua 脚本,它在服务端一次执行完毕,真正原子。


14. Redis 如何实现延时队列 / 延时任务?

口述版:

常用 ZSet 实现:

  • score 存执行时间的时间戳
  • member 存任务内容

后台线程或定时任务循环用 ZRANGEBYSCORE 取出到期的任务,处理完再删除。

也可以用 Redis 5.0 的 Stream,或者直接用专业的延时队列中间件(如 RocketMQ 延时消息)。


面试加分点

  1. 能结合自己项目讲:缓存了什么数据、过期时间怎么定、有没有遇到穿透/击穿、怎么解决的。
  2. 知道 Redisson、缓存空值、布隆过滤器、大 Key 治理这些实战手段。
  3. 能说清「最终一致性」而不是吹「强一致」。
  4. 对 Redis 6 多线程、Redis 7 新特性有简单了解更佳。

一句话总结记忆:

Redis 面试核心围绕:数据结构 → 持久化 → 过期/淘汰 → 三大缓存问题 → 分布式锁 → 高可用架构 → 大 Key 与一致性。把每块的原理和解决方案说清楚,再结合项目,基本就能过关。