Redis面试题深度拆解:从底层结构到分布式锁、缓存穿透与集群高可用
Redis 的面试题基本是后端岗位里出镜率最高的一类考题不管你是校招还是社招也不管你简历上写的是 Java、Go 还是别的语言至少会被面到两到三题。网上相关八股太多了但大多停留在“背结论”的层面真到现场被追问几句容易露馅。这篇是《7天学会Redis》的特别篇把 Redis 数据类型、分布式锁、缓存穿透、RDB/AOF、Cluster 分片、内存淘汰这些经典面试题按我实际面试和被面的经验重新拆了一遍。准备面试的人可以直接拿去当复习提纲日常工作里想彻底搞懂 Redis 底层逻辑的同学也可以照着里面的思路去验证。1. 数据类型题最容易被问住的底层结构1.1 基础答法只能算及格打开任意一篇 Redis 面试题合集第一题大概率是“说说 Redis 有哪些数据类型”。大多数候选人能数出来五种String、Hash、List、Set、ZSet然后补一句“String 存字符串Hash 存对象List 做队列Set 做去重ZSet 做排行榜”。这个答案在一年前也许能拿个及格分现在不行了基本每个面试官都会追问一句“底层是什么样的结构什么情况下会变”我面过不少人能把这五种背齐的占一半能说出“ziplist 会转成 hashtable”的已经算不错了。但这个问题往深里问真正想考察的是你对 Redis“省内存”这套设计思路的理解。Redis 是一个内存数据库内存就是钱所以它针对小数据量做了大量紧凑编码优化。1.2 内部编码与版本差异的进阶答法现在把每种数据类型的底层说透一点。String 的底层是 SDS简单动态字符串但为了省内存又分成了三种编码纯整数用 int 编码直接拿 C 语言的 long 存储长度小于等于 44 字节的字符串用 embstr字符串对象和 SDS 结构体放在同一块连续内存里只需一次内存分配超过 44 字节就升级成 raw分配两块内存。44 这个数字不是乱定的它和 Redis 对象头大小、SDS 头部大小、以及内存分配器的对齐策略都有关系简单记就是尽量让一个小字符串能塞进 64 字节的内存 chunk 里。Hash、List、Set、ZSet 在小数据量下也有类似设计。Hash 元素少时用 listpack早期版本是 ziplist当哈希字段数量超过hash-max-listpack-entries默认 128或单个字段值超过 64 字节时才转为正式的 hashtable。List 在 Redis 7.0 之前用 quicklist这是一个“ziplist 组成的链表”7.0 之后底层节点换成了 listpack。Set 在两个条件同时满足时用 intset所有元素都是整数且元素数量不超过 512否则转 hashtable。ZSet 在元素个数少且每个 member 长度短时用 listpack超过zset-max-listpack-entries默认 128或 value 超过 64 字节后转成 skiplist跳跃表加哈希表的组合。这里有一个非常重要的版本知识点面试时说出来很加分ziplist 有一个“级联更新”的问题。它的每个节点里保存了前一个节点的长度如果某一个节点被修改后变长了后面的节点就要跟着挪位置极端情况下会造成连环更新性能很差。所以 Redis 7.0 开始把 ziplist 相关场景全面换成 listpack。listpack 里每个节点不记录前一个节点的字节数而是通过自身长度来定位从根本上避免了级联更新。你如果能主动提到“7.0 之后 listpack 替换 ziplist”说明你真的在关注新版本变化而不是死记老八股。1.3 面试官真正想听到的回答方式答到这题不要光背编码要把“我会结合业务选型”这个意识也带出来。比如 Hash 适合存用户资料、商品信息这类对象它比直接缓存一个 JSON 串好在哪里好在你更新某个字段时不用整存整取可以HINCRBY只更新一个字段这对热点对象很友好。ZSet 做排行榜是最经典的套路score 放分数member 放用户 ID取 Top N 一个命令搞定。Set 可以做抽奖里的随机去重SRANDMEMBER、SPOP直接给结果。还有一个高频追问“为什么 ZSet 底层用跳跃表而不是平衡树或者数组”我的回答思路是这样的数组查排名很方便但插入删除要移动大量元素平衡树比如红黑树查找、插入、删除都是 O(logN)但实现非常复杂Redis 更看重简单可靠跳跃表用多层链表模拟二分查找平均复杂度也是 O(logN)代码实现比红黑树简单得多而且对范围查询天然友好输出有序集合时只需要沿着最底层链表走一遍。你把这个权衡讲清楚面试官一般就不会再往死里纠结了。2. 缓存穿透、击穿、雪崩三大缓存杀手的标准解法2.1 缓存穿透查询根本不存在的数据这三兄弟几乎是 Redis 缓存类面试题里必考的组合拳每年都有一批候选人在这里翻车。先说穿透它指的是大量请求在缓存和数据库里都查不到数据比如你拿一个不存在的用户 ID 去刷接口缓存里没有数据库里也没有每次请求都直接打到 DBDB 很容易被打垮。还有恶意用户专门用随机 ID 扫接口本质就是利用穿透来消耗后端资源。解法主要三种。第一种是缓存空值查不到的时候把 key 也写进缓存value 写一个特殊占位符或者 nullTTL 设短一点比如 5 分钟。这个方案简单直接缺点是大量不存在的 key 也会占用 Redis 内存而且短 TTL 到期后恶意请求依然会继续打 DB。所以实际生产里我习惯把空值的 TTL 再压一压并且配合“空值数量上限”控制。第二种是布隆过滤器在缓存前面加一层判断。布隆过滤器是一个 bit 数组加多个哈希函数的结构它的特点是“如果判断不存在那一定不存在如果判断存在可能误判”。你可以把它类比成安检口的快速通道真正有问题的人大概率会被拦下偶尔也有无辜的人被要求复查。把数据库里所有存在的 ID 预加载到布隆过滤器里请求进来先查过滤器不存在的直接返回DB 压力瞬间就下来了。使用时有几个工程细节要注意预估数据量和可接受误判率然后根据公式算出 bit 数组大小和哈希函数个数Guava 的 BloomFilter 可以直接用但要注意它不支持扩容数据增长后需要重建。第三种是接口层参数校验非法 ID、负数、超长字符串直接拒绝这是成本最低的一环。很多线上事故都是因为接口连最基本的参数校验都没做才让穿透打穿了 DB。2.2 缓存击穿热 key 过期瞬间的并发风暴穿透是查不存在的数据击穿则是某一个非常热门的 key 在过期的瞬间大量请求同时打到 DB。区别在于击穿的目标 key 是真实存在的高热度数据只是恰好过期了。典型的例子是首页 banner、某个爆款商品详情或者热点新闻详情缓存失效的那一刻成千上万的用户同时请求同一个 keyDB 直接被打穿。标准解法是互斥锁。流程是这样线程发现缓存里没有数据先尝试获取一个分布式锁比如SET key NX EX 5这种或者直接用一个专门的 lock key拿到锁的线程去查 DB回写缓存再释放锁没拿到锁的其他线程可以先 sleep 一小段时间再重试读缓存。这个方案能保证同一时刻只有一个线程查 DB。但这里有个容易踩的坑如果查 DB 的耗时超过了锁的过期时间锁提前释放其他线程又会涌进来或者回写缓存前锁就过期了导致多个线程同时重建缓存。解决办法是把锁的过期时间设得比预估 DB 查询时间宽松一些或者用 Redisson 这种自带看门狗续期的客户端。另一种方案是逻辑过期value 里塞一个过期时间的字段命中的时候判断逻辑时间是否过了如果过了就说明该重建了。此时线程不等锁先返回旧数据兜底只有拿到锁的线程去后台更新缓存。这个方案的优点是读请求不会因为加锁而阻塞适合读多写少、短暂容忍数据不一致的场景。你可以根据业务选对数据一致性要求高的用互斥锁对响应延迟要求苛刻的用逻辑过期。2.3 缓存雪崩大规模过期和 Redis 宕机雪崩和击穿的区别在于范围。击穿是单个热点 key 失效雪崩是大量 key 同一时间集体失效或者干脆整个 Redis 实例宕机了所有请求直接打到数据库。常见的触发原因有两个一是缓存设置 TTL 时没有考虑错峰比如凌晨 3 点定时任务刷新了大量商品统一 1 小时过期结果 4 点整所有 key 一起失效二是 Redis 本身挂了。针对第一类原因最简单有效的方法是 TTL 加随机值。比如基础过期时间 3600 秒每个 key 的过期时间在 3600 到 3600300 秒之间随机取一个让失效时间自然分散开。更进一步可以做多级缓存Redis 前面再放一层本地缓存Caffeine 或者 Guava本地缓存用少量内存挡住第一波流量即使 Redis 短暂不可用本地缓存还能扛几秒钟。针对 Redis 宕机核心思路是高可用和降级。生产环境至少要配主从加哨兵数据量再大一点直接上 Cluster避免单点问题。同时业务侧要有兜底策略Redis 故障时熔断回源、返回默认数据、或者限流宁可让一小部分请求失败也不能让 DB 被压垮。面试时如果你能把三个问题放在一起对比着说“穿透是查不存在、击穿是热 key 过期、雪崩是大规模失效或宕机”面试官基本就认可你理解到位了。3. 分布式锁与事务脚本面试官最喜欢追问的并发题3.1 可靠分布式锁的实现路径Redis 实现分布式锁大概是所有 Redis 面试题里翻车率最高的一道因为它的坑太多了每一步都有对应的追问。先说最原始的写法SETNX key value再EXPIRE key 5000。这个写法的问题非常明显——两个命令不是原子的如果设置锁之后、设置过期时间之前进程崩了锁永远不会释放其他线程永远拿不到锁。正确的初阶版本是一条命令搞定SET key value NX EX 5000把加锁和过期时间放在同一个命令里原子执行。但这还没完下一个灵魂拷问是“怎么安全释放锁”。如果你直接DEL key会出现一个经典事故线程 A 拿到锁业务执行时间超过了锁的过期时间锁已经自动释放了此时线程 B 拿到同一个锁开始执行业务A 执行完后DEL key把 B 的锁也删了然后 C 又拿到锁……最后发现锁完全失效。解决办法是给锁设一个唯一的 value比如 UUID释放前先比较 value 是不是自己的是才删。这个“先比较再删除”必须用 Lua 脚本保证原子性if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end面试官如果继续问“业务执行超过锁过期时间怎么办”就到了 Redisson 的知识点。Redisson 默认给锁 30 秒过期时间同时启动一个看门狗线程每隔 10 秒检查锁是否还在持有如果还在就自动续期等于把过期时间不断往后推。手动加锁时也可以传入自定义 leaseTime超过这个时间就不再续期。回答到这里锁的三个关键点——原子加锁、唯一标识防误删、续期防提前失效——就都覆盖了。还有一个容易丢分的细节是可重入性。原生SET NX是不支持可重入的同一个线程第二次加锁会失败。Redisson 用 Hash 结构实现了可重入field 存线程标识value 存重入次数加锁时HINCRBY释放锁时HINCRBY -1减到 0 才真正删除。如果你在回答里主动提一句“用 Hash 结构实现可重入计数”面试官对你的好感度会明显上升。3.2 Redis 事务是“假原子”Lua 才是真原子Redis 的事务也是高频题。你需要非常明确地告诉面试官Redis 的 MULTI/EXEC 和 MySQL 事务不是一回事它不会做回滚。MULTI 之后的命令会被放进一个队列EXEC 时依次执行执行期间不会被其他客户端插入命令但它并不能保证中间某条命令失败后回滚之前的结果。比如队列里有三条命令第二条对一个字符串执行 INCR这条会报错但第一条和第三条依然正常执行。所以准确的说法是Redis 事务保证“连续执行”不保证“要么全成功要么全失败”。这里有个补充知识点是 WATCH它实现了类似乐观锁的机制。WATCH key之后在 EXEC 之前如果 key 被其他客户端修改EXEC 会直接返回空事务放弃执行。你可以把它类比成开发里常见的 CAS 操作。我面试的时候比较喜欢听候选人把 WATCH 和分布式锁做对比WATCH 是“发现被改就不执行”分布式锁是“先抢占执行权再执行”两种思路对应不同的并发控制模型。为什么 Redis 不提供回滚这个问题很多人答不上来但其实有标准思路。Redis 作者认为大部分运行时错误是编程错误回滚并不能解决 bug而且实现回滚会让 Redis 的代码复杂度大幅提高违背了它“简单、高效”的初衷。如果业务真的需要多命令原子执行正确姿势是 Lua 脚本因为 Lua 脚本在 Redis 里是整体执行的执行期间不会被其他命令插队天然具备原子性。我在生产里处理秒杀扣库存、防误删锁都是优先用 Lua而不是 MULTI。4. 持久化RDB 与 AOF 背后的取舍逻辑4.1 RDB 快照的一切持久化题在面试里属于“看似简单、实则全是坑”的一类。RDB 是 Redis 的默认持久化方式它做的事情是定期把内存里的全量数据生成一个二进制快照文件dump.rdb。触发方式有手动和自动两种自动触发在配置里是save 900 1、save 300 10、save 60 10000表示 900 秒内至少有一个 key 变更就执行一次快照以此类推。RDB 的关键机制是 fork 子进程。执行 bgsave 时主进程 fork 出一个子进程子进程把数据写进临时 RDB 文件写完之后 rename 成正式文件覆盖旧文件。写的过程主进程不阻塞还能继续接待请求这里依赖的是操作系统的写时复制COW机制——fork 瞬间父子进程共享同一份物理内存父进程后续要修改某个内存页时操作系统会复制一份出来再改不会影响子进程正在使用的快照数据。所以问“bgsave 为什么可以不阻塞主进程”就答 COW这是一个很关键的加分点。但 RDB 的坑也不少。它毕竟是一个周期性全量快照两次快照之间的数据如果 Redis 挂了就彻底丢了。对数据丢失容忍度低的业务只开 RDB 是不行的。还有 fork 瞬间的性能问题内存越大fork 要复制的页表越多耗时越长如果实例内存是几十个 GBfork 可能卡顿几百毫秒甚至更久。大内存实例上要关注fork的耗时监控里看INFO stats的latest_fork_usec字段。4.2 AOF 追加日志与重写机制AOFAppend Only File把每次写命令以协议格式追加到文件末尾相当于记录了所有写操作。它的持久化策略有三种always 表示每条命令都 fsync 到磁盘最安全但性能最差everysec 表示每秒 fsync 一次极端场景下最多丢 1 秒数据是生产环境最常用的折中方案no 表示交给操作系统决定刷盘时机性能最好但丢失窗口不可控。AOF 有个绕不开的问题文件会越来越大。所以 Redis 提供了bgrewriteaof做日志压缩它会fork 一个子进程根据当前内存里的数据重新生成一份最精简的 AOF 文件只保留最终有效的数据状态然后把重写期间新来的写命令追加到这个新文件尾部。这里有个细节值得答重写期间主进程仍然在写旧 AOF同时还会把增量写命令记录到重写缓冲区重写完成后再把缓冲区里的命令追加到新文件保证新文件和内存状态完全一致。4.3 怎么选、怎么恢复、生产怎么配面试时把 RDB 和 AOF 的优缺点讲完之后面试官常会追问“生产环境到底怎么配”。我的回答是绝大多数情况下两个都开RDB 用于快速恢复和备份AOF 用于减少数据丢失。恢复时 Redis 会优先加载 AOF 文件因为 AOF 数据更完整但纯 AOF 文件通常很大加载速度慢所以很多生产方案会开启混合持久化把aof-use-rdb-preamble设为 yes这样 AOF 文件头部是一个 RDB 二进制快照后面再追加增量命令。加载的时候先秒级加载 RDB 部分再执行增量命令速度和完整性都兼顾。这里给一个实操经验如果你发现 Redis 启动后数据不对先别急着删文件。RDB 文件损坏可以用redis-check-rdb检查修复AOF 文件损坏可以用redis-check-aof修复后者的修复逻辑是找到最后一条不完整的写命令把文件截断到那里能挽回多少算多少。另外生产上最好定期把 RDB 文件备份到云存储或者另一台机器别让 Redis 挂了连带备份文件也没了这种事故我见过不止一次。5. 主从复制、哨兵与 Cluster高可用的三层递进5.1 主从复制全量同步与增量同步高可用题是 Java 后端岗位的常客尤其是那些招运维开发或资深工程师的团队几乎必问。第一个层级是主从复制。主库负责写从库负责读和备份从库通过replicaof命令指定主库。全量复制的流程要能完整讲出来从库发起 psync带上自己的 replication ID 和 offset主库判断需要全量复制后执行 bgsave 生成 RDB同时把这段时间的写命令存进复制缓冲区RDB 传给从库后从库清空旧数据、加载 RDB最后主库把复制缓冲区里的增量命令发给从库从库执行完两边就同步了。如果主从之间的网络只是短暂抖动全量复制太浪费了所以有了增量复制机制。主库维护一个环形缓冲区repl_backlog_buffer默认 1MB记录最近执行的写命令以及每个从库的复制偏移量。从库断线重连后带上自己的 offset主库检查如果这个 offset 还在 backlog 范围内就把缺失的命令补发过去如果从库落后太多offset 已经在缓冲区里被覆盖了那就只能重新全量复制。实际运维里从库落后过多会导致频繁的全量复制浪费大量带宽和磁盘 IO这就是为什么会强调要给从库尽量稳定的网络和足够的内存。5.2 哨兵自动故障转移是怎么干活的第二个层级是哨兵Sentinel它解决的是主库挂了自动切换的问题。最少要部署三个哨兵才能形成可靠方案为什么是三个而不是两个因为哨兵之间需要通过投票选出 leader 来执行故障转移必须达到多数派quorum。三个哨兵可以容忍一个哨兵挂掉两个哨兵一旦挂掉一个就没有多数派了整个系统就失去决策能力。故障转移的过程是这样哨兵发现主库 heartbeat 超时先标记主观下线SDOWN这是单个哨兵自己的判断然后向其他哨兵确认如果超过 quorum 个哨兵都认为主库下线就标记为客观下线ODOWN接着哨兵们选一个 leader由 leader 从从库中挑选一个数据最完整、网络最健康的实例作为新主库执行replicaof no one让它独立然后通知其他从库去复制新主库。主库恢复后会发现自己的角色变成了从库自动开始复制新主库的数据。这里有个高危话题是脑裂。主库和哨兵、从库网络分区哨兵认为主库挂了选出了新主库但旧主库并没有真正宕机它还在接收客户端的写请求。分区恢复后旧主库的数据跟新主库不一致它被迫降级为从库并清空数据这期间写入旧主库的数据就丢了。避免脑裂的工程手段是配置min-replicas-to-write和min-replicas-max-lag意思是主库写入前至少要有 N 个从库的复制延迟不超过多少秒否则拒绝写入。这样当主库被孤立时它无法满足写条件就不会接收新写请求大大缩小数据丢失窗口。这个点面试时能主动讲出来基本就是有真实生产经验的人。5.3 Redis Cluster16384 个槽位的分片世界第三层级是 Cluster真正的伸缩方案。Redis Cluster 把整个 key 空间划分为 16384 个哈希槽每个 key 通过CRC16(key) % 16384计算属于哪个槽然后由集群里的主节点分担这些槽位。客户端可以连接任意一个节点如果请求的 key 不在当前节点节点会返回 MOVED 重定向告诉客户端应该访问哪个节点支持集群模式的客户端会自动处理这种重定向。面试很喜欢问一个刁钻问题“为什么槽位数量是 16384而不是 65536 或者其他更大的数”我见过的标准解释是集群节点之间通过心跳消息互相传递槽位信息槽位数量越多心跳包里携带的 bitmap 就越大网络开销越高16384 个槽对应大约 2KB 的 bitmap而 65536 个槽对应 8KB在集群节点数量通常不会超过 1000 的情况下2KB 的心跳消息已经足够没必要多付出 4 倍带宽。另一个原因是节点数到不了那么大16384 个槽已经可以支撑上千个节点规模再多的槽位没有实际意义。Cluster 还有一个必须知道的限制它不支持跨槽多 key 操作。同一个事务或者 Lua 脚本里操作多个 key如果这些 key 不在同一个槽位会直接报 CROSSSLOT 错误。解决办法是使用 hash tag也就是把 key 写成{user:1000}.profile和{user:1000}.orderRedis 只对花括号里的内容计算哈希槽从而把相关 key 强制放到同一个槽位。设计 key 的时候就要考虑这个约束否则后面做集群迁移会非常痛苦。扩容时通过 reshard 把一部分槽从旧节点迁移到新节点迁移期间可能遇到 ASK 重定向表示数据正在迁移客户端需要先发送 ASKING 再执行命令。6. 过期策略、内存淘汰与线上性能排查6.1 过期 key 为什么不搞定时删除Redis 的过期删除策略是一个“面试书里必考但很多人背了也不理解”的题。先想一个问题为什么 Redis 不用一个定时器到点就把所有过期 key 精准删除因为一个 redis 实例里可能同时存在几十万个带过期时间的 key每个 key 挂一个定时器这个内存和上下文切换开销是不可接受的。所以 Redis 用的是惰性删除加定期删除的组合方案。惰性删除很好理解取 key 的时候才检查它是否过期过期就删除并返回空。它的缺点是如果一个 key 过期后一直没人访问它就会一直躺在内存里。定期删除就是来打扫这些“没人管”的过期 key 的Redis 每 100 毫秒会执行一次过期循环随机抽取一部分设置了过期时间的 key检查并删除已经过期的根据删除情况动态调整下一次抽取的时间如果删掉的 key 比例较高就多扫一会儿否则就休息。这套机制保证了过期 key 不会永久占用内存也不是所有过期 key 都能被及时清掉所以最终还有一层兜底——内存淘汰策略。6.2 内存淘汰策略怎么选这是很容易被忽略但线上必须配的一个参数。默认策略是 noeviction意思是一旦内存达到maxmemory上限所有写命令直接报错。如果只是把 Redis 当缓存用不配这个参数等内存耗尽整个业务都会因为写失败被拖垮。我见过有人把 Redis 当核心存储来用内存满了还开启了 noeviction结果大量写请求直接抛 OOM 错连带着业务系统一起挂那是非常惨烈的线上事故。生产上常用的淘汰策略有allkeys-lru、allkeys-lfu、volatile-lru等。它们的区别一句话就能讲清楚allkeys 是内存里所有 key 都参与淘汰volatile 是只排那些设置了过期时间的 key。LRU 是最近最少使用LFU 是最近最不常使用。网上很多说法把 LRU 和 LFU 混在一起其实它们针对的场景不同LRU 只考虑访问时间如果一个 key 在过去一段时间被高频访问但最近几分钟没被访问LRU 很可能会把它淘汰掉LFU 会统计访问频率真正的高频热点 key 可以被保留得更久。Redis 的 LRU 不是精确 LRU而是近似实现它从所有 key 里采样一小部分默认 5 个由maxmemory-samples控制从中选一个最不活跃的淘汰这样省去了维护全局有序链表的开销。Redis 4.0 开始支持近似 LFU如果你的业务热点分布比较极端可以考虑切到 allkeys-lfu。6.3 线上性能排查实录排查题在面试里不一定会问但实际工作中一旦 Redis 变慢一套清晰的排查思路比面试押题有用得多。我最常碰到的线上问题是RedisCommandTimeoutException也就是热词里那个io.lettuce.core.RedisCommandTimeoutException它代表客户端等了太久都没有拿到 Redis 响应。碰到这个异常我的排查顺序是先看慢查询日志SLOWLOG GET配置slowlog-log-slower-than可以捕获超过指定毫秒的命令再看是不是有大 key 在捣乱redis-cli --bigkeys扫描整个实例找出单个占用内存很大的 key这种 key 一次网络传输就几秒谁等谁崩溃接着看主库的INFO commandstats判断是哪些命令消耗了大量时间。大 key 的删除也是一个经典坑。直接DEL一个几百兆的 key会引起主线程阻塞因为释放内存是同步操作。Redis 4.0 以后提供了UNLINK命令它会异步释放内存主线程立刻返回真正释放内存的动作在后台线程完成所以生产上遇到大 key 必须用 UNLINK。另外一个容易被忽略的系统层问题是 Linux 的内存配置。建议把vm.overcommit_memory设置为 1避免 fork 子进程做 RDB 或 AOF 重写时因为虚拟内存不足直接失败然后把透明大页 THP 关掉echo never /sys/kernel/mm/transparent_hugepage/enabled否则 fork 后发生写时复制时大页的分配会让 Redis 出现明显的延迟抖动。这些配置散落在各种运维文档里很少有人把它们和面试题联系起来但你能在回答持久化时自然地提出来面试官会觉得你真的在线上扛过事。我个人实际面试和带人的体会是Redis 的题目看似分散其实都在考同一件事——你有没有真正理解它为什么这么设计。紧凑编码是为了省内存fork 加 COW 是为了不阻塞主线程哨兵选主是为了自动恢复16384 个槽是为了控制心跳开销。每一个方案背后的“为什么”才是面试官真正想听到的东西。准备这些题的时候别光背答案最好开个 redis-cli 把常见的命令敲一遍看看INFO memory、SLOWLOG GET、OBJECT ENCODING输出的真实数据长什么样。纸上得来终觉浅Redis 这种工程味极重的组件面试回答富不丰富往往就取决于你实际动手碰过多少。