1. Redis 五大基础类型及典型应用场景?

类型 底层 经典应用
StringSDS 动态字符串缓存、计数器(INCR)、分布式 ID(INCRBY)、SETNX 锁
Hash哈希表 + 压缩列表对象/购物车存储(field 级操作,省序列化)
List双向链表 + 压缩列表消息队列(LPUSH/BRPOP)、最新列表、简单流
Set哈希表 + 整数集合去重、共同关注(SINTER)、抽奖(SRANDMEMBER)
ZSet(有序集合)跳表 + 哈希表排行榜(按 score 排序)、延时队列(score=时间戳)、限流(滑动窗口)

进阶类型:Bitmap(签到、布隆过滤器思路)、HyperLogLog(UV 统计,~12KB 记 2^64 基数)、GEO(地理位置)、Stream(正式消息队列,支持消费者组)。

🎯 面试要点

  • String 的 SETNX + EXPIRE 是分布式锁雏形;ZSet 的 score 即"权重",排行榜秒出
  • HyperLogLog 是概率结构,有误差(0.81%);要精确去重用 Set 或布隆
  • Stream 是 Redis 5.0 引入的"正经"MQ,替代 List 消息队列(有 ACK 机制)

2. 底层实现:SDS、跳表、压缩列表?

编码转换条件(object encoding 命令可查)
# Redis 7.x 紧凑编码阈值(超过就转哈希表/跳表):
#   Hash: 元素 ≤ 512 且单个值 ≤ 64 字节 → listpack
#   ZSet: 元素 ≤ 128 且单个值 ≤ 64 字节 → listpack
# 超过 → hashtable / skiplist
# Set 全整数且 ≤ 512 个 → intset

# 检查编码
127.0.0.1:6379> OBJECT ENCODING key
# "ziplist" / "hashtable" / "skiplist" / "intset" / "embstr"

🎯 面试要点

  • Redis 7.0 用 listpack 替代 ziplist(修复连锁更新问题),原理类似:紧凑数组
  • 跳表每层概率 p=1/4 提升(ZSKIPLIST_P),最高 32 层
  • 记忆点:Redis 用内存换速度,一切底层选择(跳表/压缩列表/SDS)都是为了"快 + 省内存"

3. 什么是渐进式 rehash?为什么要渐进?

rehash:哈希表扩容/缩容时,把数据从 ht[0] 迁移到新表 ht[1],之后交换指针。若一次性迁移,大 key 场景会阻塞单线程 Redis 数十毫秒甚至秒级。

渐进式:迁移分摊到每次增删改查——每操作一个桶就顺带搬移一小批(桶 0..index),期间:

触发:负载因子(元素数/桶数)≥ 1 且允许扩容时扩容;有子进程做 BGSAVE/AOF rewrite 时会「避免扩容」,只有比值 > 5 才强制扩容(控制内存);< 0.1 时缩容。

🎯 面试要点

  • 渐进式 rehash 是"大任务切小步"的典型:单线程框架里一切长操作都要分片(类似 JVM 的并发标记)
  • 大 key 是 Redis 性能杀手:rehash、删除、迁移都会卡主线程 → 大 key 要拆分/异步删除(UNLINK)

🎤 常见面试追问

  1. ZSet 为什么用跳表不用红黑树?——实现简单、区间查询方便(从头顺序走)、内存可控、并发友好。Redis 选择了跳表。
  2. SDS 比 C 字符串好在哪里?——O(1) 取长度、自动扩容防溢出、二进制安全(能存 \0)、预分配+惰性释放减少内存分配次数。
  3. 渐进式 rehash 为什么必要?——Redis 单线程,一次性搬移大哈希表会卡住所有命令;分摊到每次操作搬几个桶,不阻塞。
  4. 大 key 有什么危害?——rehash/删除/迁移都卡主线程、内存碎片多。危害:阻塞。处理:拆分、UNLINK 异步删除、扫描用 SCAN 不用 KEYS。
  5. HyperLogLog 能精确去重吗?——不能,它是概率结构(误差约 0.81%),适合 UV 这种"数量级准确即可"的统计;要精确用 Set。

📖 名词解释(本页术语)

术语 大白话解释
String最简单的类型:字符串/数字。缓存、计数器(INCR)、SETNX 锁都靠它。
Hash一个键下多个字段(像对象):HSET user:1 name 小明。存对象信息用。
List有序可重复列表:LPUSH/RPOP。最新消息、简单队列。
Set去重集合:SADD/SINTER。共同好友、抽奖、去重。
ZSet(有序集合)带分数的有序集合:ZADD/ZRANGE。排行榜、延时队列。
SDSString 的底层实现:带长度信息的动态字符串,安全高效。
跳表(SkipList)多层有序链表,上层是索引,查找 O(log n)——ZSet 的底层。
压缩列表 / QuickList元素少时用紧凑数组省内存(List/Hash/ZSet 的小数据形态)。
渐进式 rehash哈希表扩容时分摊到每次操作搬桶,避免单线程卡顿。
大 key单个 key 数据量过大(如几 MB 的 String、百万元素的集合)——会阻塞 Redis,需要拆分。
⚠️ 本页面由 AI 生成,内容仅供参考,请以官方文档和实际源码为准。