Redis面试视频|当前热门考点全景

基于2024年各大厂最新面经提炼,聚焦高频、易错、易忽略的Redis面试核心问题,内容实时更新,拒绝过时题库。

高频考点

为什么Redis单线程却能高性能?

很多候选人误以为多线程一定更快,其实Redis的高性能源于:
① 完全基于内存操作;② 避免线程切换与锁竞争;③ 使用I/O多路复用(epoll/kqueue);④ 数据结构高效。

注意陷阱:Redis 6.0后引入了多线程I/O,但命令执行仍为单线程——这并非退步,而是为突破网络带宽瓶颈。

数据结构

SDS(简单动态字符串)如何优于C字符串?

C字符串以'\0'结尾,无法快速获取长度;而Redis的SDS:
• 存储len字段,O(1)获取长度;
• 采用空间预分配(alloc = len + free),避免频繁扩容;
• 惰性空间释放,减少内存碎片。

示例:执行APPEND时,若剩余空间足够则不重新分配,仅更新len。

持久化

RDB与AOF:如何选择?

RDB:快照模式,适合备份与灾难恢复;但可能丢失最后一次快照后的数据。
AOF:追加日志,可配置每秒/每次写入;数据更安全,但文件体积大、恢复慢。

最佳实践:主从架构中,主节点用AOF(保证数据不丢),从节点用RDB(加速重启与复制)。

高并发

缓存穿透、击穿、雪崩怎么破?

穿透:查不存在数据 → 用布隆过滤器或缓存空值(短TTL);
击穿:热点key过期 → 互斥锁或逻辑过期+异步更新;
雪崩:大量key同时过期 → 随机过期时间+二级缓存+熔断降级。

案例:某电商大促时,用SETNX实现分布式锁防止击穿,配合EXPIRE避免死锁。

集群

Redis Cluster的Gossip协议原理

集群节点间通过ping/pong消息交换状态,每秒随机PING 3个节点,再PING剩余节点中最近通信最久的1个。消息包含:
• 节点ID、IP/端口
• 标记(主/从、故障?)
• 配置Epoch
• 槽位分配信息

关键点:故障判定需半数以上节点同意,避免脑裂。

实战

分布式ID生成方案对比

• Snowflake:依赖时间戳,需解决时钟回拨
• Redis INCR:简单高效,但单点风险
• Redis Cluster分片INCR:高可用方案
• UUID:无序,查询性能差

推荐方案:基于Redis的INCRBY + 槽位分片(如:id:order:{slot}),配合TTL自动清理。

底层原理深度解析

不满足于“会用”,更要理解“为什么这样设计”。本模块系统拆解Redis核心数据结构与内存模型。

字符串与SDS
哈希结构
列表与快速排序
集合与字典
有序集合ZSet

SDS:Redis字符串的底层实现

Redis没有直接使用C语言的字符串(以'\0'结尾的字符数组),而是自定义了简单动态字符串(Simple Dynamic String, SDS),其结构如下:

  • len:已使用字节数(字符串长度)
  • alloc:分配的总字节数(含'\0')
  • buf[]:实际数据缓冲区

优势:
• O(1)获取长度;
• 减少内存分配次数(预分配机制:len < 1MB时free=alloc/2);
• 二进制安全(可存储图片、视频等二进制数据);
• 兼容部分C字符串函数(末尾自动补'\0')。

面试提示:当执行SET key "hello"后,再APPEND key "world",若原buf空间不足,SDS会扩容至2倍(不足1MB)或+1MB(超过1MB)。

哈希结构:字典与渐进式rehash

Redis的哈希(Hash)底层使用字典(Dict)实现,包含两个哈希表(ht[0]和ht[1]),用于渐进式rehash:

  • 插入新键:只写入ht[1],ht[0]保持只读
  • 查询/更新:先查ht[0],未找到再查ht[1]
  • 渐进式rehash:每次操作时迁移1~N个槽位,避免阻塞
  • 触发条件:负载因子 > 1(ht[0])或 > 5(ht[1])

典型问题:为什么不用一次性rehash?
答:避免长阻塞。1000万键的字典,一次性rehash可能卡住数秒,而渐进式可平滑过渡。

列表结构:QuickList与ziplist优化

Redis列表(List)底层为QuickList结构,由双向链表 + 压缩列表(ziplist)组成:

  • QuickListNode:每个节点是一个ziplist
  • ziplist:紧凑内存结构,适合小数据(默认阈值:512字节/项数)
  • 配置项:list-max-ziplist-size(默认-2,表示8KB)

示例:
LPUSH users user1 user2 user3 → 若初始ziplist未超限,直接插入;超限时分裂为两个ziplist节点。

注意:ziplist会因插入/删除触发连锁更新(连续反转),故大列表建议用链表或Stream。

集合:intset与hashtable的切换

Redis集合(Set)底层有两种实现:

  • intset:整数集合,适用于全为整数且数量≤512的情况(内存更省)
  • hashtable:哈希表,支持任意类型,O(1)操作

升级规则:
• 新增元素为非整数 → 自动升级为hashtable
• 元素数>512 → 强制升级

示例:
SADD numbers 1 2 3 → 使用intset;
SADD numbers 1.5 → 转为hashtable,所有整数转为字符串。

有序集合:跳跃表(Skip List)原理

ZSet底层由dict + skiplist组成:

  • dict:维护member→score映射(O(1)查分值)
  • skiplist:按score排序,支持O(logN)范围查询

跳跃表结构:
• 每层链表节点数约为下层一半
• 插入时随机生成层数(1~32层)
• 头节点层数为32,实际平均层数≈2~3

为什么不用AVL树?
答:跳跃表实现更简单,区间查询更高效(无需旋转),且并发友好(Redis单线程无需复杂锁)。

Redis面试真题演进路径

从初级到高级,层层递进掌握Redis面试核心逻辑。附真实面试场景与参考回答思路。

初级(1~2年经验)

Q:Redis有哪些基本数据类型?

5种核心类型:String、Hash、List、Set、ZSet;2种特殊结构:Bitmap、HyperLogLog;Stream(Redis 5.0新增)。

参考回答:
“String是二进制安全的,可存储文本、图片;Hash适合对象存储;List用于消息队列;Set用于去重;ZSet用于排行榜。”
中级(3~5年经验)

Q:如何保证缓存与数据库的双写一致性?

常见方案:
① 先更新DB→再删缓存(推荐,避免脏数据);
② 串行化写操作;
③ 使用 Canal 监听DB变更;
④ 读写分离场景下,读从库可能延迟。

真实案例:
某金融系统曾因“先删缓存再更新DB”导致并发时读到旧DB数据,回填缓存污染,后改用“更新DB成功后再删缓存”解决。
高级(5年+)

Q:Redis集群脑裂时如何保证数据一致性?

Redis Cluster通过quorum(多数派)机制防止脑裂:
• 故障节点需半数以上主节点投票才能标记为故障
• 分区外节点无法写入(因槽位不可达)
• 客户端需配置cluster-require-full-coverage no(允许部分槽位不可用)

关键配置:
min-replicas-to-write 1:至少1个从节点在线才允许写入
min-replicas-max-lag 10:从节点延迟超10秒不参与写入投票
架构师级

Q:如何设计一个高可用的Redis缓存架构?

分层策略:
① 主从 + 哨兵/Cluster(高可用层)
② 本地缓存(Caffeine) + Redis(多级缓存)
③ 熔断降级(Sentinel限流)
④ 异步写回 + 事务日志(持久化保障)
⑤ 监控告警(延迟、内存、连接数)

某电商架构实践:
商品详情页:本地缓存TTL=2min → Redis集群(3主3从)→ MySQL集群
订单系统:Redis Stream做削峰 → 持久化AOF → 异步写入DB

网友们还关心这些问题

以下内容基于2024年各大技术社区真实提问整理,涵盖Redis面试延伸场景与避坑指南。