一、 基础与数据结构
1. 什么是 Redis?它的主要特点是什么?
-
解答: Redis(Remote Dictionary Server)是一个基于内存的高性能 Key-Value 数据库。
-
主要特点:
- 速度快: 数据存在内存中,读写速度极快(单机 QPS 可达 10 万+)。
- 数据类型丰富: 支持 String、List、Hash、Set、ZSet(有序集合),以及 Bitmaps、HyperLogLog、Geo 等高级结构。
- 支持持久化: 可以将内存数据异步保存到硬盘中(RDB 和 AOF)。
- 高可用与分布式: 支持主从复制、哨兵模式和 Cluster 集群,提供高可用方案。
2. Redis 为什么这么快?
-
解答: 主要有三个原因:
- 纯内存操作: 避免了磁盘 I/O 的耗时。
- 单线程模型(核心网络处理): 避免了多线程上下文切换的开销,也不存在多线程竞争导致的锁问题。(注:Redis 6.0 引入了多线程处理网络请求,但核心的命令执行依然是单线程)。
- I/O 多路复用: 采用了 epoll 机制,允许单线程同时处理多个客户端的网络连接,最大化网络吞吐量。
3. Redis 常用的数据类型及应用场景有哪些?
-
解答:
- String(字符串): 最基础的类型。常用于缓存对象、计数器(文章阅读量)、分布式锁。
- Hash(哈希): 适合存储对象(如用户信息)。比起将整个对象序列化成 String,修改特定字段时性能更好。
- List(列表): 双向链表。常用于消息队列(非强一致性要求)、最新动态展示(Timeline)。
- Set(集合): 元素唯一且无序。常用于抽奖系统、共同好友推荐(交集、并集操作)。
- ZSet(有序集合): 元素唯一且带有权重(Score)。常用于排行榜(如游戏积分榜、微博热搜)。
4. Redis 的单线程模型会有什么问题?有什么改进?
- 解答: 单线程最大的问题是阻塞。如果执行某个非常耗时的命令(如
KEYS *或删除超大 Key),会导致整个 Redis 服务卡顿,无法处理其他请求。 - 改进: 从 Redis 4.0 开始引入了
UNLINK命令进行异步删除;Redis 6.0 引入了网络 I/O 多线程,将网络读写和解析交由多个线程处理,但命令执行部分仍然保持单线程,以此提升网络并发能力。
二、 内存与持久化
5. Redis 的持久化机制有哪些?
-
解答: 主要有两种:RDB(快照)和 AOF(追加文件)。
- RDB: 在指定时间间隔内,将内存中的数据生成快照并保存到磁盘。优点是恢复速度快,文件体积小,适合全量备份;缺点是可能会丢失最后一次快照后的数据。
- AOF: 将每一个写命令以追加的方式写入日志文件。优点是数据安全性更高(最多丢失一秒数据);缺点是文件体积大,恢复速度比 RDB 慢。Redis 4.0 引入了混合持久化机制,结合了二者的优点。
6. RDB 触发机制是怎样的?会阻塞主线程吗?
-
解答: RDB 可以通过
SAVE或BGSAVE触发。SAVE:在主线程执行,会严格阻塞 Redis,期间无法处理其他命令,生产环境严禁使用。BGSAVE:主线程会fork出一个子进程来完成全量同步,主线程不会阻塞。但fork操作本身在数据量极大时可能会造成短暂阻塞。
7. Redis 的内存淘汰策略有哪些?
-
解答: 当 Redis 内存达到设定的最大值(maxmemory)时,会触发淘汰机制。主要有 8 种,常见的分为两类:
- 不淘汰:
noeviction(默认配置),内存满了直接返回错误。 - 按 LRU(最近最少使用)淘汰:
allkeys-lru(所有键中挑选最少使用的淘汰)、volatile-lru(只在设置了过期时间的键中淘汰)。 - 按 LFU(最不经常使用)淘汰:
allkeys-lfu、volatile-lfu。 - 随机淘汰:
allkeys-random、volatile-random。 - 按过期时间淘汰:
volatile-ttl(优先淘汰即将过期的键)。
- 不淘汰:
8. Redis 怎么处理过期键?(过期策略)
-
解答: Redis 结合了定期删除和惰性删除两种策略:
- 惰性删除: 客户端访问某个 Key 时,Redis 先检查它是否过期,如果过期就删除并返回空。这对 CPU 友好,但如果过期 Key 没被访问,会一直占用内存。
- 定期删除: Redis 默认每秒执行 10 次,随机抽取部分设置了过期时间的 Key 进行检查并删除已过期的。这能有效释放内存,避免冷数据堆积。
三、 经典缓存问题
9. 什么是缓存穿透?如何解决?
-
解答: 缓存穿透是指查询一个根本不存在的数据。由于缓存没命中,请求每次都会打到数据库,导致数据库压力剧增。
-
解决方案:
- 缓存空对象: 即便数据库返回空,也把这个空值缓存起来(设置较短的过期时间)。
- 布隆过滤器(Bloom Filter): 在请求到达缓存前,先通过布隆过滤器判断数据是否存在。若判断不存在,则直接返回,不查数据库。
10. 什么是缓存击穿?如何解决?
-
解答: 缓存击穿是指某一个热点 Key 在缓存过期的瞬间,同时有大量的并发请求打过来,导致数据库瞬间被压垮。
-
解决方案:
- 互斥锁(Mutex Key): 只允许一个线程去数据库查询数据并重建缓存,其他线程等待重建完成后再从缓存读取。
- 逻辑过期: 不给热点 Key 设置物理过期时间,而是在 Value 中存入一个逻辑时间。发现逻辑过期后,异步开启一个线程去更新缓存,当前请求返回旧数据。
11. 什么是缓存雪崩?如何解决?
-
解答: 缓存雪崩是指在某一个时间段,大批量的 Key 同时过期,或者 Redis 宕机,导致大量请求直接打到数据库,引发数据库崩溃。
-
解决方案:
- 过期时间打散: 给不同 Key 的过期时间加上一个随机值,避免集中失效。
- 高可用架构: 使用 Redis Sentinel 或 Cluster 保证 Redis 集群的高可用。
- 限流降级: 数据库压力过大时,实施服务降级,牺牲部分用户体验保护核心系统。
四、 并发与一致性
12. 如何保证缓存与数据库双写时的数据一致性?
-
解答: 最经典的方案是 Cache Aside Pattern(旁路缓存模式) :
- 读: 先读缓存,命中则返回;未命中则查数据库,将数据放入缓存后返回。
- 写: 先更新数据库,再删除缓存。
- 为什么不是更新缓存? 相比删除,更新缓存的并发控制极难,且会造成无效计算。
- 极端一致性需求: 如果需要强一致性,可以引入延迟双删策略,或者使用中间件订阅数据库的 Binlog(如 Canal)去异步更新/删除 Redis。
13. Redis 如何实现分布式锁?
-
解答: 最基本的实现是使用
SET resource_name my_random_value NX PX 30000命令。- NX: 表示不存在才创建(加锁)。
- PX: 设置过期时间(防止客户端宕机导致死锁)。
- 解锁: 必须使用 Lua 脚本保证原子性,判断 Value 是自己设置的那个随机值才执行删除(防止误删别人的锁)。
- 进阶: 在生产环境中,通常推荐使用 Redisson 框架,它内置了看门狗机制(Watchdog) ,可以自动为未执行完业务的锁续期。
14. Redis 事务有什么特性?支持回滚吗?
-
解答: Redis 通过
MULTI、EXEC、DISCARD和WATCH实现事务。- 它将多个命令打包,按顺序在单线程中执行,期间不会被其他客户端打断。
- 不支持回滚: 这是一个核心考点。如果事务中的某个命令在执行时出错(如对 String 执行 HASH 操作),Redis 会继续执行后续命令,不会回滚之前的操作。Redis 认为错误是编程逻辑引起的,为了追求高性能,放弃了复杂的回滚机制。
五、 高可用与集群架构
15. Redis 主从复制的原理是什么?
-
解答:
- 全量复制: 从节点第一次连接主节点时,主节点会执行
bgsave生成 RDB 文件发送给从节点,从节点加载 RDB。在此期间产生的新写命令会放在缓冲队列中,后续再发给从节点。 - 增量复制: 断线重连后,主节点只需把断开期间的写命令(根据偏移量 offset 在 repl_backlog_buffer 中查找)发给从节点即可。
- 全量复制: 从节点第一次连接主节点时,主节点会执行
16. 什么是 Redis 哨兵(Sentinel)?
-
解答: 哨兵是 Redis 高可用的解决方案,主要解决主从复制中需要人工介入故障转移的问题。
- 主要功能: 监控(检查主从是否健康)、自动故障转移(主节点挂了,自动将某个从节点提升为主节点)、通知(将新的主节点信息告诉客户端)。
17. Redis Cluster 集群的原理是什么?
-
解答: Redis Cluster 是一种去中心化的集群架构,解决了单机内存和并发的瓶颈。
- 它引入了**哈希槽(Hash Slot)**的概念,共有 16384 个槽。
- 集群将这些槽平均分配给各个节点。当客户端存取 Key 时,Redis 会根据 CRC16 算法计算出结果再对 16384 取模,找到对应的槽位,从而将请求路由到负责该槽位的节点上。
18. 什么是脑裂(Split-Brain)现象?Redis 如何解决?
- 解答: 脑裂是指因为网络分区,导致主节点和从节点/哨兵之间断开,哨兵选举出了新主节点,但旧主节点仍能接收客户端请求。网络恢复后,旧主节点变成从节点同步新主节点数据,导致脑裂期间写入旧主节点的数据全部丢失。
- 解决方案: 调整
min-replicas-to-write和min-replicas-max-lag参数。要求主节点至少有 N 个从节点,且主从延迟不能超过 M 秒,否则主节点拒绝写入操作,从而有效降低数据丢失的风险。
六、 高级特性与底层原理
19. Redis 中的跳跃表(Skip List)是什么?
-
解答: 跳跃表是 ZSet(有序集合)的底层数据结构之一。
- 由于普通的有序链表查询效率低,跳跃表在链表的基础上增加了多级索引(类似于二分查找的思想),通过在上一层索引跨越多个节点,大大提高了查询效率。其时间复杂度与红黑树类似,为 O(log N),但实现比红黑树简单很多。
20. 什么是大 Key(Big Key)问题?如何排查和解决?
-
解答: 大 Key 不是指 Key 名字长,而是指 Value 过大(如一个 String 大于 10KB,或者一个 Hash/List 包含了上万个元素)。
- 危害: 读写大 Key 会占用大量带宽和 CPU,导致 Redis 阻塞,甚至引发网络拥塞。
- 排查: 使用
redis-cli --bigkeys命令扫描,或者通过分析 RDB 文件。 - 解决: 拆分数据(比如将一个大 Hash 拆分成多个小 Hash);清理时使用
UNLINK异步删除,避免主线程卡死。