Redis 面试问答

发布时间:2026/7/17 12:33:04
Redis 面试问答 1. 为什么要 RedisMySQL 数据在磁盘上高并发读热点时 QPS 容易顶不住。Redis 把数据放内存读写是微秒级一般当加速层挂在 MySQL 前面——扛热点读、分担压力不是拿来替代主库的。2. 常见应用场景场景结构说明数据缓存String / Hash热点先查 Redismiss 再查 DB 回填Session / 登录态String TTL多实例共享登录信息计数String INCR点赞、浏览量原子递增排行榜ZSet按 score 排序取 Top N分布式锁String SET NX EX防重复执行、扣库存互斥限流INCR EXPIRE同一用户每分钟最多 N 次缓存是最常见的用法。要做可靠消息队列还是 RabbitMQ / KafkaRedis 的 List / Stream 只适合量小、能丢几条的场景。3. 什么样的数据适合放缓存不是所有数据都值得进 Redis大致看四条读多写少商品详情、用户昵称头像——一天读几万次改几次热点明显爆款 SKU、活动页配置——少数 key 扛大部分流量能容忍短暂旧数据类目树、系统配置——晚几秒同步问题不大体积可控单条 KB 级总量算得出内存装得下反过来账户余额、库存扣减这种强一致的得靠 DB订单流水、操作日志写多读少塞缓存命中率低半年没人看的冷数据占了内存也白占个性化推荐每人结果不同缓存 key 会爆炸。拿电商商品详情举例同一个productId被大量重复访问内容不常改查 Redis 命中直接返回miss 再查 MySQL 回填TTL 设 30 分钟就行。用户改昵称后先更新 DB再删user:{id}缓存下次读自动回填新值。4. 为什么快根本原因是内存比磁盘快几个数量级。另外几个点面试也会问单线程执行命令——读写串行不用加锁、不用切线程Redis 6 之后网络 IO 可以多线程但命令还是单线程跑IO 多路复用——一个线程监听大量连接谁有数据处理谁专用数据结构——String、Hash、ZSet 按场景优化过不是一张通用表包打天下。5. 五种类型类型典型用法String缓存 JSON、计数、分布式锁Hash一个对象多个字段比如用户 profileList简单队列、最新消息Set去重、共同好友、标签ZSet排行榜按 score 排序和 Memcached 比Memcached 只有 String、不能持久化Redis 类型多、能落盘、集群方案成熟新项目基本选 Redis。6. 持久化数据在内存里重启就没了。RDB 是隔一段时间拍快照恢复快但两次快照之间的写入可能丢。AOF 是记每条写命令像写日记丢得少文件大、恢复慢。生产里常见做法是混合快照打底日志补最近的增量。7. 缓存穿透、击穿、雪崩用缓存绕不开这三个问题。穿透是有人查 DB 里根本不存在的 id缓存永远 miss请求全打到 DB。简单做法是在 DB 也查不到时往 Redis 存一个 nullTTL 设短一点同一个恶意 id 再来就直接挡掉。合法 id 很多、伪造请求又多的话可以在前面加布隆过滤器——一种很省内存的「可能存在名单」把库里有的 id 预先放进去它说「不存在」就一定不存在直接返回说「可能存在」再去查 Redis 和 DB有极低概率误报。布隆过滤器不是 Redis 内置类型要用得装 RedisBloom 模块或者在应用层自己维护。小项目缓存空值通常就够。击穿是热点 key 到期被 Redis 删掉了同一瞬间大量请求一起 miss、打穿 DB。可以加互斥锁过期后只允许一个线程回源查 DB、重建缓存其余等着或重试。还有一种叫逻辑过期的做法key 本身不设 TTL在 value 里自己存过期时间比如{ expireAt: 1710003600, data: {...} }。读的时候如果业务上判断已过期仍然先把旧 data 返回给用户同时另起线程异步去 DB 拉新的——key 不会在 Redis 里突然消失就不会出现全员同时 miss 的情况。代价是实现复杂些过期窗口内用户可能看到稍旧的数据。雪崩是大量 key 同一时刻过期或者 Redis 整机挂了DB 被冲垮。给 TTL 加随机偏移错开过期时间配合集群和限流降级。8. 缓存和 DB 怎么一致最常用的是Cache Aside读的时候先查缓存没有就查 DB 再回填写的时候先更新 DB再删缓存——注意是删不是改直接改缓存容易在并发下乱序。这只能做到最终一致删完缓存到下次读之间可能还有旧值强一致还是得靠 DB。9. 分布式锁单机lock关键字在多实例部署时管不了别的机器需要有个大家都能访问的地方抢锁Redis 是常见选择。用在哪定时任务三台机器都配了凌晨 2 点跑报表加锁后只有一台真正跑用户连点两次支付第二次拿不到锁直接拒绝同一订单的状态流转只允许一个流程在处理。秒杀扣库存高并发时有时用 Redis Lua 原子扣减比锁更轻锁更适合「整段业务同一时刻只允许一个进来」的场景。QPS 极高、或者金融账务强一致的锁不是首选。怎么做加锁一条命令SET lock:stock:10086 随机UUID NX EX 30NX表示 key 不存在才设成功抢到了设不成功说明别人占着。EX 30是 30 秒自动过期进程崩溃没释放也不会死锁。value 用 UUID释放时才知道删的是不是自己的锁。业务跑完释放锁不能先 GET 再 DEL——中间锁可能过期被别人抢走你再 DEL 就删了别人的。得用 Lua 把「比对 value 删除」合成一步ifredis.call(GET,KEYS[1])ARGV[1]thenreturnredis.call(DEL,KEYS[1])elsereturn0end业务跑得比 TTL 长就起个看门狗线程定期续期。另外锁是互斥不是排队拿不到就失败或重试主从切换极端情况下锁可能丢要求绝对可靠得上 ZooKeeper或者业务本身做幂等。10. 主从、Sentinel、Cluster单机 Redis 扛读压力或者想要备份会上主从一主多从写走主主异步把数据推给从从可以分担读。复制是异步的主不等从确认就返回所以性能好但从可能落后——刚写主库立刻读从库可能还是旧值。主挂了纯主从得人工把某个从升为主生产一般会继续加Sentinel。Sentinel 是独立部署的监控进程通常起奇数个比如 3 个盯着主从节点的健康主挂了自动投票选一个从升为新主再通知客户端改连新地址。它解决的是「主节点故障怎么自动切换」不管数据分片也不解决单机内存不够。数据量再大、一台机器的内存就不够用了得上Cluster把数据分到多台机器上存——每台只存一部分加起来才是完整的数据。Cluster 怎么决定某个 key 存哪台机器它把 key 空间切成16384 个槽slot可以理解成 16384 个编号格子0 号到 16383 号。每个 key 根据名字算出一个数对 16384 取模落到某个格子上这个格子分配给哪台机器key 就在哪台机器上。比如 05000 号槽给 A 机器500110000 给 B 机器。客户端连任意一台都能问如果连错了机器对方会告诉你「这个 key 不在我这去另一台」这就是MOVED。扩容缩容要搬槽的时候搬迁过程中会临时回ASK意思是「这数据正在搬你先去找目标机器但路由表还没完全更新」。有个限制一次操作如果要动多个 key比如 MGET、事务这些 key 必须在同一个槽里否则 Redis 拒绝执行。想让两个 key 进同一个槽可以用hash tag——key 名里加{}只有花括号里的部分参与算槽号。比如user:1001:profile和user:1001:orders可能落在不同机器改成{1001}:profile和{1001}:orders花括号里都是1001就会算到同一个槽。Cluster 里每个负责一部分数据的主节点也可以有自己的从节点某台主挂了它的从能顶上来和前面说的主从 failover 是一个思路只是多了一层「先分片、再高可用」。实际选型很简单数据一台内存够、怕主挂主从 Sentinel数据超单机、要扩容上 Cluster开发小项目单机够用。Cluster 运维和客户端都麻烦没到这个量级不必硬上。11. 单线程、大 key、热 key前面说了命令是单线程执行的一个慢操作会堵住后面所有请求。KEYS *扫全库、过长的 Lua 脚本都是雷区遍历用SCAN分批来。删 key 也要注意。DEL是同步删key 摘掉的同时主线程当场释放内存删完才返回UNLINK是异步删key 立刻从字典里摘掉、客户端已经查不到了真正 free 内存交给后台线程慢慢做。一个 Hash 存了百万字段、或者 String 塞了几 MB JSON用 DEL 可能卡主线程几百毫秒这时用 UNLINK主线程微秒级就返回了。小 key 两者差别不大。大 key本身就要从设计上拆开存删的时候用 UNLINK迁移也慢都是同一个问题。热 key是少数 key 扛了绝大部分流量比如明星商品可以在前面加本地缓存或者多副本分散读即使用了 Cluster同一个 key 也只在一个节点上分片挡不住单 key 过热。12. 过期与内存淘汰key 设了 TTL 也不会精确到毫秒删除。有人访问时发现过期了才删这叫惰性删除后台还会随机抽一批 key 检查过期叫定期删除。所以过期 key 可能多存活一小会儿。内存设了maxmemory上限之后满了就要走淘汰策略。缓存场景常用 allkeys-lru淘汰最久没用的或 allkeys-lfu淘汰访问最少的。13. Pipeline、事务、LuaPipeline 是客户端一口气发多条命令减少网络往返但不保证原子只是打包传输。MULTI / EXEC 让多条命令连续执行、中间不被插队但不支持回滚。需要「读-改-写」一体原子的时候用 Lua 脚本Redis 把整段脚本当一个命令执行。14. Pub/Sub发布订阅发布者往频道channel发消息订阅了这个频道的客户端都会收到彼此不知道对方是谁——典型的发布订阅模型。Redis 用PUBLISH/SUBSCRIBE实现。缺点是消息不持久化订阅者不在线就直接丢了也没有 ack 确认所以不可靠。要可靠队列用 Stream或者 RabbitMQ / Kafka。