05-面试题库

48 阅读12分钟

Redis 题库

50 道精选 Redis 题,涵盖基础到进阶

📋 目录


基础篇

1. Redis 是什么?有什么特点?

答案: Redis 是一个开源的内存数据结构存储系统,可作为数据库、缓存和消息队列。

特点

  • 高性能(10万+ QPS)
  • 丰富的数据类型
  • 原子性操作
  • 持久化支持
  • 主从复制、哨兵、集群

2. Redis 为什么这么快?

答案

  1. 纯内存操作:数据存储在内存中
  2. 单线程模型:避免线程切换和锁竞争
  3. IO 多路复用:epoll/kqueue 高效处理网络 IO
  4. 高效数据结构:底层使用优化的数据结构

3. Redis 单线程为什么还这么快?

答案

  • 避免上下文切换:无线程切换开销
  • 避免锁竞争:无需加锁
  • CPU 不是瓶颈:Redis 的瓶颈是内存和网络
  • IO 多路复用:高效处理多个连接

注意:Redis 6.0 引入了 IO 多线程,但命令执行仍是单线程。


4. Redis 和 Memcached 的区别?

答案

特性RedisMemcached
数据类型String、Hash、List、Set、ZSet只支持 String
持久化RDB、AOF不支持
分布式集群、哨兵客户端分片
过期策略多种淘汰策略LRU
线程模型单线程 + IO 多路复用多线程

5. Redis 的应用场景有哪些?

答案

  1. 缓存:热点数据缓存
  2. Session 存储:用户会话
  3. 计数器:点赞数、浏览量
  4. 排行榜:游戏排行、热搜
  5. 消息队列:任务队列、延时队列
  6. 分布式锁:防止重复提交
  7. 限流:接口限流、防刷

6. Redis 支持的数据类型有哪些?

答案基本类型

  • String:字符串
  • Hash:哈希表
  • List:列表
  • Set:集合
  • Sorted Set:有序集合

特殊类型

  • Bitmap:位图
  • HyperLogLog:基数统计
  • Geo:地理位置
  • Stream:数据流

7. Redis 的过期键删除策略?

答案

  1. 惰性删除

    • 访问键时检查是否过期
    • 优点:节省 CPU
    • 缺点:占用内存
  2. 定期删除

    • 每秒 10 次随机检查过期键
    • 优点:平衡 CPU 和内存
  3. 主动删除

    • 内存不足时触发淘汰策略

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

答案

# 8 种淘汰策略
noeviction        # 不淘汰,返回错误(默认)
allkeys-lru       # 从所有键中淘汰最近最少使用(推荐)
allkeys-lfu       # 从所有键中淘汰最不经常使用
allkeys-random    # 从所有键中随机淘汰
volatile-lru      # 从设置过期时间的键中淘汰 LRU
volatile-lfu      # 从设置过期时间的键中淘汰 LFU
volatile-random   # 从设置过期时间的键中随机淘汰
volatile-ttl      # 淘汰即将过期的键

推荐allkeys-lru


9. Redis 事务的特点?

答案命令:MULTI、EXEC、DISCARD、WATCH

特点

  • 原子性:要么全执行,要么全不执行
  • 隔离性:串行执行,不会被打断
  • 无回滚:命令错误不会回滚

注意:Redis 事务不支持回滚!


10. Redis 的 Pipeline 是什么?

答案: Pipeline 将多个命令打包一次性发送,减少网络往返。

优点

  • 减少网络延迟
  • 提高吞吐量

注意

  • Pipeline 不保证原子性
  • 不宜一次性发送太多命令

11. WATCH 命令的作用?

答案: WATCH 用于实现乐观锁。

原理

  • 监视一个或多个键
  • 如果键被修改,事务将失败

示例

WATCH key
GET key
MULTI
SET key newvalue
EXEC  # 如果 key 被其他客户端修改,返回 nil

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

答案

# 加锁
SET lock:order:1 uuid123 NX EX 10

# 解锁(使用 Lua 保证原子性)
if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("DEL", KEYS[1])
else
    return 0
end

要点

  • 使用 SET NX EX 加锁
  • 设置过期时间防止死锁
  • 使用 Lua 脚本释放锁保证原子性
  • 锁的值用唯一标识(UUID)

13. Redis 的发布订阅?

答案

# 订阅频道
SUBSCRIBE channel1

# 发布消息
PUBLISH channel1 "message"

# 模式订阅
PSUBSCRIBE news.*

特点

  • 消息不持久化
  • 无消息确认
  • 适合实时通知

14. Redis keys 命令为什么不推荐使用?

答案原因

  • 单线程阻塞:keys 操作会阻塞 Redis
  • O(n) 复杂度:键越多越慢
  • 生产环境可能导致服务不可用

替代方案

# 使用 SCAN 游标迭代
SCAN 0 MATCH pattern COUNT 100

15. Redis 如何实现限流?

答案方案 1:固定窗口(INCR + EXPIRE)

local current = redis.call('INCR', KEYS[1])
if current == 1 then
    redis.call('EXPIRE', KEYS[1], ARGV[1])
end
return current <= tonumber(ARGV[2]) and 1 or 0

方案 2:滑动窗口(Sorted Set)

# 添加请求记录
ZADD rate:user:1 timestamp timestamp
# 删除过期记录
ZREMRANGEBYSCORE rate:user:1 0 (timestamp-60000)
# 统计数量
ZCARD rate:user:1

数据类型篇

16. String 的应用场景?

答案

  • 缓存:热点数据
  • 计数器:点赞数、浏览量
  • Session:用户会话
  • 分布式锁:SETNX
  • 限流:INCR + EXPIRE

17. Hash 的应用场景?

答案

  • 用户信息:HSET user:1 name "Alice"
  • 商品信息:HSET product:101 price 99
  • 购物车:HSET cart:user:1 product:101 2

优势

  • 节省内存(相比多个 String)
  • 字段独立操作

18. List 的应用场景?

答案

  • 消息队列:LPUSH + BRPOP
  • 时间线:微博、朋友圈
  • 最新列表:最新文章、最新评论
  • 栈:LPUSH + LPOP
  • 队列:LPUSH + RPOP

19. Set 的应用场景?

答案

  • 标签系统:文章标签、用户兴趣
  • 共同好友:SINTER
  • 去重:自动去重
  • 抽奖系统:SPOP 随机抽取
  • 推荐系统:基于标签推荐

20. Sorted Set 的应用场景?

答案

  • 排行榜:游戏排行、成绩排名
  • 热搜榜:按热度排序
  • 延时队列:按时间戳排序
  • 权重排序:按权重值排序

21. Bitmap 的应用场景?

答案

  • 签到统计:每天一位
  • 在线用户统计:10 亿用户 = 119MB
  • 活跃用户统计:按天/周/月统计
  • 布隆过滤器:判断元素是否存在

22. HyperLogLog 的应用场景?

答案

  • UV 统计:网站独立访客
  • 搜索引擎:查询去重
  • 大数据:海量数据去重计数

特点

  • 12KB 可统计 2^64 个元素
  • 0.81% 误差率

23. Geo 的应用场景?

答案

  • LBS 应用:附近的人、附近的商家
  • 打车软件:查找附近的司机
  • 外卖平台:查找附近的餐厅

24. Stream 的应用场景?

答案

  • 消息队列:替代 List
  • 日志系统:实时日志流
  • 事件溯源:事件流存储

优势

  • 消息持久化
  • 消费者组
  • 消息确认

25. 如何选择合适的数据类型?

答案

需求数据类型
简单键值String
对象存储Hash
列表、队列List
去重、集合运算Set
排行榜Sorted Set
签到统计Bitmap
UV 统计HyperLogLog
地理位置Geo
消息队列Stream

持久化篇

26. Redis 持久化方式有哪些?

答案

  1. RDB(快照)

    • 定期保存内存快照
    • 文件小,恢复快
    • 可能丢失最后一次快照后的数据
  2. AOF(追加文件)

    • 记录每个写命令
    • 数据更安全(最多丢 1 秒)
    • 文件大,恢复慢
  3. 混合持久化

    • AOF 重写时先写 RDB,再追加增量命令
    • 兼具两者优势

27. RDB 和 AOF 如何选择?

答案

场景推荐方案
数据不能丢AOF (everysec)
可接受分钟级数据丢失RDB
追求性能RDB
兼顾安全和性能混合持久化

28. RDB 的触发方式?

答案自动触发

save 900 1       # 900 秒内至少 1 次修改
save 300 10      # 300 秒内至少 10 次修改
save 60 10000    # 60 秒内至少 10000 次修改

手动触发

SAVE      # 同步保存(阻塞)
BGSAVE    # 异步保存(fork 子进程)

29. AOF 的同步策略?

答案

appendfsync always    # 每个命令都同步(慢但最安全)
appendfsync everysec  # 每秒同步(推荐)
appendfsync no        # 由操作系统决定(快但可能丢数据)

推荐everysec,平衡性能和安全性。


30. AOF 重写是什么?

答案: AOF 文件会越来越大,重写可以压缩 AOF 文件。

原理

  • 遍历内存中的数据
  • 生成最少的命令
  • 替换原 AOF 文件

触发

# 手动
BGREWRITEAOF

# 自动
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

高可用篇

31. Redis 主从复制原理?

答案全量同步

  1. 从节点发送 PSYNC 命令
  2. 主节点执行 BGSAVE 生成 RDB
  3. 主节点发送 RDB 文件
  4. 从节点清空数据并加载 RDB
  5. 主节点发送增量命令

增量同步

  • 主节点将写命令发送到复制缓冲区
  • 从节点接收并执行命令

32. Redis 哨兵的作用?

答案

  1. 监控:检查主从节点是否正常
  2. 通知:故障时通知管理员
  3. 自动故障转移:主节点故障时自动切换
  4. 配置提供:客户端获取主节点地址

33. 哨兵如何判断节点下线?

答案主观下线(Subjectively Down)

  • 单个哨兵认为节点下线
  • 超过 down-after-milliseconds 未响应

客观下线(Objectively Down)

  • 多数哨兵认为节点下线
  • 达到 quorum 数量

34. 哨兵故障转移流程?

答案

  1. 主观下线:单个哨兵认为主节点下线
  2. 客观下线:多数哨兵认为主节点下线
  3. 选举领导哨兵:Raft 算法选举
  4. 故障转移
    • 选择一个从节点提升为主节点
    • 其他从节点复制新主节点
    • 通知客户端新主节点地址

35. Redis 集群如何分片?

答案

  • 16384 个槽位
  • CRC16 算法slot = CRC16(key) % 16384
  • 槽位分配给不同节点

36. Redis 集群如何扩容?

答案

  1. 添加新节点
  2. 重新分配槽位
  3. 迁移数据
# 添加节点
redis-cli --cluster add-node new_host:port existing_host:port

# 重新分配槽位
redis-cli --cluster reshard existing_host:port

37. Redis 集群的优缺点?

答案优点

  • 水平扩展
  • 高可用
  • 自动故障转移

缺点

  • 不支持多键操作(除非在同一槽位)
  • 客户端需要支持集群协议
  • 运维复杂度高

38. 如何保证 Redis 高可用?

答案

  1. 主从复制:数据备份
  2. 哨兵模式:自动故障转移
  3. 集群模式:分片 + 高可用
  4. 持久化:RDB + AOF
  5. 监控告警:及时发现问题

39. Redis 脑裂问题?

答案脑裂:网络分区导致同时存在多个主节点。

后果:数据不一致、数据丢失。

解决方案

# 要求至少 1 个从节点在线
min-replicas-to-write 1
# 延迟不超过 10 秒
min-replicas-max-lag 10

40. Redis 如何实现读写分离?

答案

  • 主节点:处理写请求
  • 从节点:处理读请求

注意

  • 主从同步有延迟
  • 可能读到旧数据
  • 适合对一致性要求不高的场景

性能优化篇

41. 什么是大 Key?如何解决?

答案大 Key

  • String:值大于 10KB
  • Hash/List/Set/ZSet:元素超过 5000

危害

  • 阻塞其他命令
  • 内存不均衡
  • 删除耗时

解决方案

  1. 拆分大 Key
  2. 使用 UNLINK 异步删除
  3. 监控大 Key(redis-cli --bigkeys)

42. 什么是热 Key?如何解决?

答案热 Key:频繁访问的 Key。

危害

  • 单点压力大
  • 可能导致节点崩溃

解决方案

  1. 本地缓存
  2. 多副本(key_replica_1, key_replica_2)
  3. 读写分离

43. Redis 慢查询如何排查?

答案

# 查看慢查询
SLOWLOG GET 10

# 配置
CONFIG SET slowlog-log-slower-than 10000  # 10ms
CONFIG SET slowlog-max-len 128

优化

  • 避免 KEYS *,改用 SCAN
  • 避免 HGETALL 大 Hash,改用 HSCAN
  • 使用 Pipeline 批量操作

44. Redis 内存占用过高如何优化?

答案

  1. 设置最大内存:maxmemory 1gb
  2. 淘汰策略:allkeys-lru
  3. 压缩数据:使用 Hash 代替 String
  4. 设置过期时间:EXPIRE
  5. 清理无用数据:定期清理

45. Redis 如何做性能测试?

答案

# redis-benchmark
redis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 100000

# 参数
-c: 并发连接数
-n: 请求数
-d: 数据大小
-t: 测试命令(SET,GET,INCR等)

实战篇

46. 如何解决缓存穿透?

答案问题:查询不存在的数据,缓存和数据库都没有。

解决方案

  1. 缓存空值
data = cache.get(key)
if data is None:
    data = db.get(key)
    if data is None:
        cache.set(key, 'NULL', 60)  # 缓存空值
  1. 布隆过滤器
if not bloom_filter.exists(key):
    return None  # 一定不存在

47. 如何解决缓存击穿?

答案问题:热点 Key 过期,瞬间大量请求打到数据库。

解决方案

  1. 互斥锁
if cache.get(key) is None:
    if lock.acquire():
        data = db.get(key)
        cache.set(key, data)
        lock.release()
  1. 永不过期(逻辑过期):
data = cache.get(key)
if data['expire'] < now:
    async_update_cache(key)  # 异步更新
return data['value']

48. 如何解决缓存雪崩?

答案问题:大量 Key 同时过期,数据库压力骤增。

解决方案

  1. 过期时间加随机值
expire = 3600 + random.randint(0, 300)
cache.set(key, data, expire)
  1. 多级缓存

    • Redis + 本地缓存
  2. 限流降级

    • 数据库限流
    • 返回降级数据

49. 如何实现延时队列?

答案使用 Sorted Set

# 添加任务(时间戳作为分数)
ZADD delay:queue 1698048000 "task1"

# 获取到期的任务
ZRANGEBYSCORE delay:queue 0 current_timestamp LIMIT 0 100

# 删除已处理的任务
ZREM delay:queue "task1"

Lua 脚本实现

local tasks = redis.call('ZRANGEBYSCORE', KEYS[1], 0, ARGV[1], 'LIMIT', 0, 100)
for i, task in ipairs(tasks) do
    redis.call('ZREM', KEYS[1], task)
end
return tasks

50. Redis 在项目中遇到过什么问题?

答案示例 1:缓存击穿

  • 问题:热点商品缓存过期,瞬间大量请求打到数据库
  • 解决:使用分布式锁 + 双重检查

示例 2:大 Key

  • 问题:单个 Hash 存储 10万条数据,查询慢
  • 解决:拆分为多个小 Hash

示例 3:内存不足

  • 问题:Redis 内存占用过高
  • 解决:设置 maxmemory + allkeys-lru 淘汰策略

总结

Redis 核心:

  1. ✅ 理解 Redis 原理(单线程、IO 多路复用)
  2. ✅ 掌握数据类型和应用场景
  3. ✅ 熟悉持久化机制(RDB、AOF)
  4. ✅ 了解高可用方案(主从、哨兵、集群)
  5. ✅ 能解决缓存问题(穿透、击穿、雪崩)
  6. ✅ 会使用 Lua 脚本
  7. ✅ 有实际项目经验

建议

  • 理论结合实践
  • 举例说明
  • 量化指标(QPS、响应时间等)
  • 总结踩过的坑

祝你顺利! 🎉