Redis 常见面试题梳理从基础概念到实战问题Redis 面试题看起来很多但核心其实很集中Redis 为什么快、有哪些数据结构、怎样保证数据不丢、如何扩展容量和可用性以及在缓存、分布式锁、消息队列等场景中会踩哪些坑。本文按照课件第十一章的顺序把常见问题整理成一篇适合复习和发布的博客。1. 什么是 Redis它有什么特点Redis 是一个高性能的 key-value 内存数据库。它和 MySQL 这类关系型数据库不同不使用“表”来组织数据而是通过键值对来存储数据。Redis 的数据主要放在内存中因此读写速度非常快同时它也支持把数据持久化到磁盘避免服务重启后数据完全丢失。Redis 牺牲了关系型数据库中的复杂查询、强约束、复杂事务等能力换来了简单、高效和灵活。它的主要特点包括使用内存存储读写性能高。支持多种数据结构。支持 RDB、AOF 等持久化机制。命令执行主要是单线程模型。支持主从复制。支持哨兵模式。支持集群模式。支持事务。支持多语言客户端。面试回答时不必死背定义可以围绕“内存数据库、key-value、多数据结构、高性能、可持久化、支持高可用和集群”这几个关键词展开。2. Redis 支持哪些数据类型Redis 最核心的五种数据类型是String字符串最基础的数据类型。List列表适合队列、栈等场景。Hash哈希适合存储对象。Set集合适合去重、共同好友、标签等场景。ZSet有序集合适合排行榜、权重排序等场景。后续版本还加入了一些特定场景的数据类型Bitmap通过二进制位表示状态。Bitfield把字符串当作位图并进行位操作。HyperLogLog适合做基数统计空间占用很小。Geospatial存储经纬度并进行地理位置计算。Stream消息流常用于消息队列场景。前五种类型通用性最强面试中出现频率最高后面的类型更偏专项能力知道适用场景即可。3. Redis 数据类型底层的编码方式是什么Redis 的每一种数据类型底层都可能有多种编码方式。Redis 会根据数据规模和元素特征自动选择合适的内部编码。常见对应关系如下数据类型常见内部编码stringraw、int、embstrhashhashtable、ziplist / listpacklistlinkedlist / quicklist、ziplistsethashtable、intsetzsetskiplist、ziplist / listpack几个重点概念embstr短字符串的优化实现课件中提到小于等于 39 字节时可能使用。raw普通字符串编码适合较长字符串。ziplist压缩列表本质是连续内存节省空间适合元素少且元素短的场景。listpackRedis 7 开始引入的新结构用来替代 ziplist。intset整数集合适合元素都是整数且数量较少的 set。skiplist跳表常用于有序集合。quicklist由多个压缩列表组成的链表是 list 的重要实现方式。可以使用下面的命令查看某个 key 的实际编码object encoding key4. ZSet 为什么使用跳表而不是红黑树跳表和红黑树在插入、查询、删除上的时间复杂度都可以达到O(logN)但跳表实现更简单也不需要像红黑树那样进行复杂的旋转和重新平衡。对于 Redis 来说跳表足够高效而且工程实现成本更低所以 ZSet 选择跳表作为重要的底层结构。5. Redis 的常见应用场景有哪些Redis 常见应用场景包括缓存缓存热点数据减少数据库访问压力。计数器统计播放量、点赞数、访问数等。排行榜基于 ZSet 很容易实现分数排序。分布式会话把 session 放到 Redis多个服务节点共享。分布式锁通过 Redis 控制分布式系统中的并发访问。消息队列可以使用 List、Pub/Sub 或 Stream 实现简单消息队列。这些场景都有一个共同点要么需要高性能读写要么需要 Redis 提供的特定数据结构能力。6. 怎样测试 Redis 服务器的连通性使用ping命令即可redis-cliping如果 Redis 正常连通会返回PONG这是排查 Redis 服务是否启动、网络是否通畅、客户端是否能连接的最基本方式。7. 如何设置 key 的过期时间有两种常见方式。第一种是在set时直接指定setname zhangsan ex60表示name这个 key 60 秒后过期。第二种是先设置 key再单独使用expiresetname zhangsan expire name60如果需要查看剩余过期时间可以使用ttl name8. Redis 为什么采用单线程模型Redis 的性能瓶颈通常不在 CPU而在内存和网络 IO。Redis 内部逻辑相对简单如果为了命令执行引入多线程收益不一定明显反而会增加线程安全、锁竞争、上下文切换等复杂度。Redis 使用单线程执行命令可以让数据结构实现更简单也能避免多线程并发带来的很多问题。需要注意的是Redis 6.0 之后引入了多线程 IO但多线程主要用于网络数据读写和协议解析真正执行 Redis 命令仍然主要是单线程完成。9. Redis 里的 IO 多路复用是怎么回事IO 多路复用可以理解为用一个线程同时管理多个 socket 连接当某个连接真正有数据到来时再通知线程去处理。Linux 中常见的 IO 多路复用实现包括selectpollepollRedis 主要基于 epoll 实现高效网络 IO。epoll 可以在内核中管理大量连接并在连接有事件发生时通知用户线程处理。这样 Redis 不需要为每个客户端连接都创建一个线程从而减少线程资源浪费。这也是 Redis 单线程模型仍然能够支撑高并发的重要原因之一。10. Redis 的持久化机制有哪些Redis 持久化主要有两种RDB快照持久化在某个时间点把内存数据生成快照文件。AOF追加日志持久化把写命令追加到日志文件中。RDB 文件更紧凑适合备份和快速恢复AOF 记录更细数据丢失风险相对更低。实际生产中可以根据业务要求选择也可以两者结合使用。11. RDB 的持久化触发条件有哪些RDB 可以手动触发也可以自动触发。手动触发方式save bgsave其中save会阻塞 Redis 主线程bgsave会创建子进程生成 RDB 文件主进程继续处理请求。自动触发方式包括在配置文件中通过save m n配置例如 m 秒内发生 n 次修改就触发。从节点进行全量复制时触发。执行shutdown关闭 Redis 时触发。12. AOF 的文件同步策略有哪些AOF 的同步策略主要有三种策略含义always每次写入 AOF 缓冲区后都调用fsync同步到磁盘最安全但性能最低everysec写入后先由操作系统缓存每秒同步一次性能和安全性较均衡no只写入操作系统缓存由操作系统决定何时刷盘性能最好但风险最大生产中最常见的是everysec它通常能在性能和数据安全之间取得较好的平衡。13. AOF 的重写机制是怎样的AOF 会不断追加写命令文件会越来越大。AOF 重写的作用是在不改变最终数据结果的前提下用更少的命令重新描述当前数据集。例如一个计数器执行了很多次incrAOF 中可能记录了大量命令重写后可以变成一条最终的set命令。这样可以减小 AOF 文件体积提高恢复速度。14. Redis 的 key 过期删除策略是怎样的Redis 同时使用惰性删除和定期删除。惰性删除是指访问某个 key 时Redis 才检查它是否过期如果已经过期就删除它。这种方式节省 CPU但如果过期 key 长时间没人访问可能会继续占用内存。定期删除是指Redis 周期性抽样检查设置了过期时间的 key删除其中已经过期的 key。它在 CPU 和内存之间做了折中。所以 Redis 不是一到过期时间就立刻删除所有 key而是通过“访问时检查 定期抽样检查”的方式处理过期数据。15. 大量 key 在同一时间过期会产生什么问题如何处理如果大量 key 的过期时间设置得过于集中到期时 Redis 需要删除大量 key可能造成短暂卡顿。Redis 的定期删除会限制单次删除的最大耗时避免长时间阻塞但在对延迟要求很高的业务中即使几十毫秒的停顿也可能影响明显。解决思路是给过期时间增加随机值让 key 的过期时间分散开。例如原本都设置为 2 秒过期可以调整为2 秒 随机 0 到 500 毫秒这样可以减少同一瞬间大量 key 同时过期的风险。16. Redis 的淘汰策略是怎样的当 Redis 内存不足并且继续写入新数据时会根据配置的淘汰策略处理旧 key。常见淘汰策略包括volatile-lru从设置了过期时间的 key 中淘汰最近最少使用的 key。allkeys-lru从所有 key 中淘汰最近最少使用的 key。volatile-lfu从设置了过期时间的 key 中淘汰最近使用频率最低的 key。allkeys-lfu从所有 key 中淘汰最近使用频率最低的 key。volatile-random从设置了过期时间的 key 中随机淘汰。allkeys-random从所有 key 中随机淘汰。volatile-ttl从设置了过期时间的 key 中优先淘汰更早过期的 key。noeviction不淘汰写入新数据时报错。LRU 表示 Least Recently Used即最近最少使用LFU 表示 Least Frequently Used即最近使用频率最低。生产环境中默认策略noeviction往往更容易暴露问题。因为 Redis 不应该等到内存完全耗尽才处理应该依赖监控和告警提前扩容或优化。17. Redis 如果内存用完了会发生什么Redis 内存达到上限后如果继续写入数据会触发淘汰策略。具体结果取决于maxmemory-policy的配置如果配置了 LRU、LFU、random、ttl 等策略会删除一部分 key。如果是noeviction写请求会失败并返回错误。因此生产环境一定要设置合理的内存上限并配合监控系统观察内存使用趋势。18. Redis 为什么把数据放到内存中核心原因是效率。内存访问速度远快于磁盘访问速度。课件中给出的对比很直观一次内存随机访问大约是 100ns而一次磁盘寻道可能达到 10,000,000ns差距是多个数量级。把数据放到内存中让 Redis 在读写性能上有明显优势。但内存也有缺点空间比磁盘小。掉电后数据会丢失。因此 Redis 一方面以内存为主保证性能另一方面通过 RDB、AOF 做持久化降低数据丢失风险。19. Redis 的主从同步 / 主从复制是怎么回事主从复制用于解决两个问题提高可用性主节点故障时还有从节点保存数据副本。分担读压力读多写少场景下可以让从节点承担部分读请求。部署时一个 Redis 节点作为主节点其他节点作为从节点。从节点启动后通过配置指定主节点然后复制主节点的数据。后续主节点数据发生变化从节点也会同步更新。一般情况下主节点可以读写从节点主要负责读。这样可以降低主节点压力。20. Redis 的 pipeline 是什么正常情况下客户端执行多条命令每条命令都要经历一次请求和响应也就是一次 RTT。命令数量多时大量时间会消耗在网络往返上。pipeline 可以把多条 Redis 命令合并到一次请求中发送给服务端服务端依次执行后再统一返回结果从而减少网络开销。需要注意pipeline 不是事务不保证这批命令原子执行。它只是减少网络往返提高批量命令执行效率。如果一次 pipeline 中命令太多也可能让 Redis 阻塞所以要控制批量大小。21. Redis 哨兵是什么哨兵 Sentinel 是 Redis 的高可用方案。它主要负责监控主从节点状态并在主节点故障时自动完成故障转移。哨兵的核心作用包括监控 Redis 主节点和从节点是否正常。主节点故障时从从节点中选举新的主节点。通知客户端新的主节点地址。让其他从节点切换复制新的主节点。哨兵解决的是“主节点挂了之后需要人工切换”的问题。22. Redis 哨兵发现主节点宕机后会做什么大致流程是哨兵通过心跳检测发现主节点疑似下线。多个哨兵达成一致后确认主节点客观下线。哨兵之间选举出一个 leader。leader 从从节点中选择一个合适的节点提升为新主节点。让其他从节点改为复制新的主节点。通知客户端主节点已经发生切换。这个过程就是故障自动转移。23. Redis 集群是干什么的主从复制和哨兵主要解决可用性问题但不能真正扩展单个 Redis 主节点的存储容量。Redis 集群用于解决数据分片和水平扩展问题。Redis Cluster 会把数据分散到多个主节点上每个主节点负责一部分数据。这样可以突破单机内存上限也可以分摊读写压力。24. Redis 的哈希槽是怎么回事Redis Cluster 把整个数据空间划分为 16384 个哈希槽。每个 key 会根据哈希规则映射到某个槽再由负责该槽的节点存储。可以简单理解为key - hash - slot - node通过哈希槽Redis Cluster 能够把不同 key 分散到不同节点实现数据分片。25. Redis 集群的最大节点个数是多少课件中提到作者建议集群节点数量不要超过 1000 个。节点数量过多会增加集群通信和维护成本反而影响稳定性。实际生产中也不会盲目堆节点而是根据数据量、访问量、机器配置、运维复杂度综合设计集群规模。26. Redis 集群会丢失操作吗Redis 集群不能保证强一致性在某些极端情况下可能丢失写操作。例如客户端写入某个 key 后主节点还没来得及把数据同步给从节点也没来得及写入 AOF此时主节点宕机新的主节点并没有这条数据那么这次写入就可能丢失。所以 Redis 更偏向高性能和最终一致性不适合对强一致要求极高的核心交易场景。27. Redis 集群如何选择数据库Redis 单机模式可以通过select命令选择不同数据库例如 0、1、2 等。但 Redis Cluster 不支持选择数据库只能使用默认的 0 号数据库。28. 什么是一致性哈希算法一致性哈希是一种常见的数据分片算法。它把节点和数据都映射到一个哈希环上数据顺时针找到离自己最近的节点进行存储。一致性哈希的优点是当节点增加或减少时不需要让所有数据重新分布只会影响环上一小部分数据。Redis Cluster 使用的是哈希槽机制不是传统一致性哈希但它们解决的问题类似都是为了让数据能够分散到多个节点。29. Redis 事务和 MySQL 事务有什么区别Redis 事务通过MULTI、EXEC等命令把一组命令打包执行。它更像是“批量顺序执行”而不是 MySQL 那种完整事务。主要区别Redis 事务没有复杂的回滚机制。Redis 事务不保证像 MySQL 那样的强原子性。Redis 事务执行期间命令会按顺序执行。可以配合WATCH实现乐观锁效果。所以 Redis 事务适合简单批量操作不适合替代关系型数据库的复杂事务。30. Redis 和事务相关的命令有哪些常见事务命令包括MULTI开启事务。EXEC执行事务中的命令。DISCARD取消事务。WATCH监控 key用于实现乐观锁。31. 为什么生产环境不应该使用keys *keys *会扫描 Redis 中所有 key类似于数据库中的全表扫描。如果 key 数量很大这个命令会耗时很长。由于 Redis 命令执行主要是单线程的一个耗时命令会阻塞后续命令导致线上请求延迟升高甚至造成故障。生产环境中需要遍历 key 时应优先使用scan渐进式遍历。32. 如何使用 Redis 作为消息队列常见做法有三种。第一种是使用 List。生产者通过LPUSH或RPUSH写入消息消费者通过BLPOP或BRPOP阻塞式读取消息。第二种是使用 Pub/Sub。发布者发送消息订阅者接收消息适合实时广播但消息可靠性较弱。第三种是使用 Stream。Stream 是 Redis 5 引入的数据类型功能比 List 和 Pub/Sub 更完整更适合消息流场景。不过如果业务对消息可靠性、堆积、重试、消费组等要求很高通常还是更建议使用专业 MQ。33. 如何使用 Redis 实现分布式锁最基本思路是使用set nx exsetlock_key unique_value nx ex10含义是只有 key 不存在时才设置成功并且设置过期时间避免业务异常导致锁永远不释放。释放锁时要注意只能释放自己加的锁所以 value 通常要设置为唯一标识删除前先判断 value 是否匹配。实际项目中不建议手写复杂分布式锁可以使用 Redisson 等成熟库。34. 什么是缓存穿透、缓存雪崩、缓存击穿缓存穿透是指查询的数据在缓存和数据库中都不存在请求每次都会打到数据库。常见解决方案是缓存空值、使用布隆过滤器。缓存雪崩是指大量 key 在同一时间过期或者 Redis 整体不可用导致大量请求同时打到数据库。常见解决方案是过期时间加随机值、多级缓存、限流降级。缓存击穿是指某个热点 key 过期大量请求同时访问这个 key瞬间打到数据库。常见解决方案是热点 key 永不过期、互斥锁重建缓存、逻辑过期。这三个问题都和“缓存失效后流量打到数据库”有关但触发原因不同。35. 什么是热 key 问题如何解决热 key 是指某个 key 的访问频率特别高甚至单独一个 key 就能把某个 Redis 分片打满。Redis Cluster 可以分散不同 key 的压力但同一个 key 只能落到同一个分片上所以热 key 很容易造成局部压力过大。常见解决思路扩大集群规模尤其是增加热 key 所在分片的从节点。在应用层识别热 key并进行二次哈希把一个逻辑 key 拆成多个物理 key。将热 key 单独放到独立 Redis 集群中。使用应用本地缓存减少访问 Redis 的次数。36. 如何实现 Redis 高可用Redis 高可用通常依赖三类能力主从复制提供数据副本和读压力分摊。哨兵监控主节点故障时自动切换。集群实现数据分片和水平扩展。简单说主从解决副本问题哨兵解决自动故障转移问题集群解决容量扩展问题。37. Redis 和 MySQL 如何保证双写一致性双写一致性指的是用户修改数据时既要修改数据库也要让缓存中的数据保持正确。直接“更新数据库 更新 Redis”容易出现不一致因为这两个操作不是原子的。更常见的做法是删除缓存而不是直接更新缓存。方案一是延时双删先删除缓存。再更新数据库。延迟一小段时间后再次删除缓存。第二次删除是为了兜底降低并发场景下出现脏数据的概率。方案二是删除缓存失败后重试。比如删除失败时把 key 放入 MQ后续不断重试删除。更工程化的做法可以结合 MySQL binlog例如使用 Canal 监听数据库变更再删除或更新缓存。38. 生成 RDB 期间Redis 是否可以处理写请求取决于触发 RDB 的方式。如果使用saveRedis 主线程会被阻塞期间无法处理外部写请求。如果使用bgsaveRedis 会 fork 子进程生成 RDB 文件父进程继续处理请求。因此bgsave期间 Redis 通常仍然可以处理写请求。不过在bgsave之后新写入的数据不一定会进入当前这份 RDB 文件可能要等下一轮持久化。39. Redis 的常用管理命令有哪些常见管理命令包括dbsize info monitorshutdownconfig get parameter configsetparameter value debug object key flushdb flushall其中dbsize查看当前数据库 key 数量。info查看 Redis 状态和统计信息。monitor实时监听 Redis 收到的请求生产环境谨慎使用。shutdown保存数据并关闭 Redis。config get/set查看或设置配置项。flushdb清空当前数据库危险命令。flushall清空所有数据库危险命令。debug segfault这类命令会制造服务崩溃只能在测试环境理解用途不能在生产环境随意执行。40. Redis 使用的网络通信协议是什么Redis 客户端和服务端之间使用 Redis Serialization Protocol通常简称 RESP。课件中写作 RSP可以理解为同一个协议点。RESP 是一种简单、高效、易解析的应用层协议用于客户端和 Redis 服务端之间传输命令和响应。41. Redis 如何遍历 keykeys *可以一次性获取所有 key但会阻塞 Redis不适合生产环境。更推荐使用scanscan cursor[match pattern][count count]scan是渐进式遍历每次返回一部分 key同时返回下一次遍历使用的游标。当游标返回 0 时说明遍历结束。示例scan0count100同类命令还有hscan遍历 hash。sscan遍历 set。zscan遍历 zset。需要注意scan 遍历过程中如果 key 发生变化可能出现重复或遗漏所以业务上要能接受这种弱一致性。42. Redis 如何实现“查找附近的人”可以使用 Geospatial 类型存储地理位置。写入位置geoadd people116.39712839.916527user1查询附近成员georadius people116.39712839.9165271km这样就可以查询某个经纬度附近一定范围内的成员。新版本中也可以使用GEOSEARCH等命令完成类似查询。43. 什么是 Redis 的 bigkey 问题如何解决bigkey 指某个 key 对应的 value 占用空间过大。例如一个 String 存了特别大的文本或二进制内容。一个 Hash 中字段特别多。一个 Set 或 ZSet 中元素数量特别大。bigkey 会带来几个问题读写耗时增加。删除时可能阻塞 Redis。集群模式下容易造成数据倾斜。网络传输压力变大。解决思路是拆分把一个大 key 拆成多个小 key每个 key 只保存一部分数据。排查 bigkey 可以使用redis-cli--bigkeys删除 bigkey 时不要直接使用del因为同步删除可能阻塞 Redis。更推荐使用unlink keyunlink会异步释放内存对 Redis 主线程影响更小。总结Redis 面试题虽然覆盖面广但可以归纳为几条主线基础能力数据结构、内部编码、命令使用。性能来源内存存储、单线程模型、IO 多路复用、pipeline。数据安全RDB、AOF、过期删除、淘汰策略。高可用与扩展主从复制、哨兵、集群、哈希槽。业务实践缓存问题、分布式锁、消息队列、双写一致性、热 key、bigkey。复习时不要只背结论最好能围绕“为什么这么设计、解决了什么问题、又带来了什么代价”来理解。这样不管面试官从基础命令问起还是追问线上故障处理都能把答案串起来。