欢迎光临
我们一直在努力

吃透这篇 Redis 面试题,搞定 90% 后端面试(2026 超全整理)

吃透这篇 Redis 面试题,搞定 90% 后端面试(2026 超全整理)

涵盖:基础原理、数据类型、持久化、高可用、性能优化、分布式实战,附面试应答技巧

一、基础原理篇

1.1 Redis为什么这么快?(必考高频)

标准回答应包含四个层面:

维度核心要点深度解析
内存存储 数据在内存中 内存读写速度纳秒级,比磁盘快千倍以上。Redis所有数据常驻内存,这是性能基础
单线程模型 核心命令单线程执行 避免多线程的上下文切换(每次切换约消耗1-2μs)和锁竞争开销。注意:Redis 6.0+的多线程仅用于网络I/O
I/O多路复用 epoll机制 单线程可处理万级并发连接,非阻塞I/O + 事件驱动架构
高效数据结构 SDS/跳表/压缩列表 专门为性能优化的底层实现,如SDS预分配策略减少内存重分配
追问1:Redis 6.0+引入了多线程,为什么不把核心命令改成多线程?

核心原因是避免锁竞争。Redis的数据结构(如字典、跳表)并非线程安全,若核心命令多线程执行需加锁,反而引入更高开销。6.0的多线程仅用于网络I/O(收发包、协议解析),核心命令执行仍是单线程。

追问2:单线程模型如何处理高并发?

单线程是指"处理客户端请求的主线程"是单线程的,但Redis通过I/O多路复用机制(epoll),可以在一个线程内监听多个socket的读写事件。当事件就绪时,主线程顺序执行这些命令。这种"非阻塞I/O + 事件驱动"的设计使单线程也能处理万级QPS。


1.2 Redis的过期键删除策略

Redis采用定期删除 + 惰性删除的组合策略:

策略原理优点缺点
惰性删除 访问key时检查是否过期,过期则删除 CPU友好,只在访问时触发 内存不友好,过期key可能长期占用内存
定期删除 每秒执行server.hz次(默认10次),每次抽查若干key 平衡内存和CPU 可能存在漏删
定期删除的算法细节(深度内容)
  • 从current_db指向的数据库开始,遍历所有数据库

  • 每次随机抽取ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP个key(默认20个)

  • 删除其中过期的key,统计过期比例

  • 如果过期比例 > 25%,继续抽查该数据库;否则切换到下一个数据库

  • 单次执行时间不超过25ms

  • 追问:为什么不用定时删除(到期立即删除)?

    定时删除需要为每个key创建定时器,若有10万个过期key就需要10万个定时器,CPU开销极大,会拖垮Redis主线程。这是典型的"用CPU换内存"策略,Redis选择了平衡方案。


    1.3 Redis内存淘汰策略

    当内存达到maxmemory阈值时触发:

    策略适用范围说明适用场景
    noeviction 内存不足时新写入报错(默认策略,生产必须修改) 不允许丢失数据的场景
    allkeys-lru 所有key 移除最近最少使用的(生产最常用) 通用缓存场景
    allkeys-lfu 所有key 移除最不经常使用的(Redis 4.0+) 热点数据有明显访问频率差异
    allkeys-random 所有key 随机移除 简单场景
    volatile-lru 仅设过期时间的key 移除最近最少使用 只淘汰部分key
    volatile-ttl 仅设过期时间的key 移除快过期的key 自动优先淘汰临近过期的
    生产推荐:allkeys-lru 或 volatile-lru,根据业务需求选择。

    二、数据类型与应用场景篇

    2.1 Redis核心数据类型及底层结构

    类型底层数据结构(Redis 7.x)经典应用场景
    String SDS(简单动态字符串) 缓存对象、计数器、分布式锁、Session共享
    Hash listpack + hashtable 存储对象(如用户信息),适合部分字段修改
    List quicklist(双向链表+压缩列表) 消息队列、文章列表、时间轴
    Set intset + hashtable 抽奖、点赞用户(去重)、社交关系(交并差集)
    ZSet listpack + skiplist(跳表) 排行榜、延时队列、带权重调度
    Bitmap SDS 用户签到、在线状态(极省空间)
    HyperLogLog SDS + 概率计数 UV统计(百万数据仅占12KB)
    Geo GeoHash + ZSet 附近的人、距离计算
    Stream radix tree + listpack 消息队列(支持消费组)、事件溯源
    ⚠️ 面试技巧:不仅要说出类型,更要结合具体业务场景说明选择理由。例如:“在消息去重场景中,Set的SADD/SISMEMBER是O(1)时间复杂度,比关系型数据库性能提升2个数量级”。

    2.2 各数据类型底层实现深度解析

    (1)String – SDS(Simple Dynamic String)

    struct sdshdr {
    int len; // 已使用长度
    int free; // 未使用长度
    char buf[]; // 字节数组
    };

    SDS的优势:

    • O(1)获取字符串长度

    • 杜绝缓冲区溢出

    • 减少内存重分配次数(空间预分配 + 惰性空间释放)

    • 二进制安全(可存储图片、序列化对象)

    (2)ZSet – 跳表(SkipList)

    为什么ZSet用跳表而不是平衡树?

    • 跳表实现更简单(约300行代码 vs 红黑树上千行)

    • 范围查询效率高(跳表O(logN)遍历,平衡树需要中序遍历)

    • 内存占用可接受(每节点约2-4个指针)

    跳表结构解析:

    typedef struct zskiplistNode {
    sds ele; // 成员对象
    double score; // 分值
    struct zskiplistNode *backward; // 后退指针
    struct zskiplistLevel {
    struct zskiplistNode *forward; // 前进指针
    unsigned int span; // 跨度
    } level[]; // 层(数组)
    } zskiplistNode;

    ZSet的双重实现策略:

    • 当元素数量 < 128 且 成员长度 < 64字节时,使用listpack编码(压缩列表,节省内存)

    • 超过阈值后,使用dict + skiplist编码:dict提供O(1)成员查找,skiplist提供O(logN)范围查询

    (3)List – QuickList

    Redis 3.2以后List使用quicklist实现,它是双向链表 + 压缩列表的组合:

    • 每个quicklist节点是一个压缩列表(ziplist)

    • 节点之间用双向指针连接

    • 平衡了内存效率和访问性能


    2.3 如何查找以固定前缀开头的key?

    # ❌ 错误方式:KEYS prefix* (会阻塞主线程,线上禁用!)
    # ✅ 正确方式:SCAN(渐进式遍历)
    SCAN 0 MATCH prefix* COUNT 100

    SCAN的特点:

    • 无阻塞,每次只返回少量结果

    • 可能有重复,客户端需去重

    • 支持增量迭代,不保存状态

    三、持久化篇

    3.1 RDB和AOF的深度对比

    特性RDBAOF
    原理 定时生成内存快照(fork子进程) 记录每条写命令到日志文件
    文件格式 二进制压缩格式 文本格式(Redis协议)
    恢复速度 快(直接加载到内存) 慢(需重放所有命令)
    数据安全性 可能丢失最后一次快照后的数据 最多丢失1秒(everysec配置)
    文件大小 小(压缩二进制) 大(需定期重写)
    CPU/内存开销 fork时可能有开销(Copy-on-Write) 持续写入,重写时开销大
    适用场景 允许少量数据丢失、追求快速恢复 数据安全性要求高
    RDB触发方式:

    # 自动触发(redis.conf)
    save 900 1 # 900秒内至少1个key变化
    save 300 10 # 300秒内至少10个key变化
    save 60 10000 # 60秒内至少10000个key变化

    # 手动触发
    BGSAVE # 后台异步生成(生产推荐)
    SAVE # 阻塞主线程(禁用!)

    AOF三种同步策略:

    appendfsync always # 每次写命令都刷盘(最安全,性能最低)
    appendfsync everysec # 每秒刷盘(推荐,最多丢1秒数据)
    appendfsync no # 由操作系统决定(最快,但可能丢大量数据)


    3.2 混合持久化(Redis 4.0+)

    核心原理:AOF重写时,将当前内存数据以RDB格式写入AOF文件开头,后续增量操作用AOF格式追加。

    文件结构:

    [RDB Header][RDB Data – 全量快照][AOF Commands – 增量命令]

    恢复流程:

  • 先加载RDB部分,快速还原基础数据

  • 再重放AOF命令,补全增量修改

  • 优势对比:

    对比项纯RDB纯AOF混合持久化
    恢复速度 较快
    数据安全 可能丢较多 最多丢1秒 最多丢1秒
    文件大小 中等
    配置开启:

    aof-use-rdb-preamble yes

    生产建议:

    • 数据安全性要求高:appendfsync everysec + 开启混合持久化

    • 夜间低峰期执行RDB备份(做灾备)

    • 定期执行BGREWRITEAOF压缩AOF文件


    3.3 fork操作对Redis性能的影响

    Copy-on-Write(写时复制)机制:

    • fork子进程时,父进程和子进程共享内存页

    • 只有当父进程或子进程修改内存页时,才复制该页

    • 正常情况下,fork耗时与内存大小成正比(每GB约100-200ms)

    优化建议:

    • 控制单实例内存大小(建议不超过16GB)

    • 避免在业务高峰期执行BGSAVE/BGREWRITEAOF

    • 使用更快的存储介质(SSD)

    • 考虑关闭rdbcompression减少CPU开销(换取更大文件)

    四、高可用篇

    4.1 Redis三种高可用模式对比

    模式核心能力节点角色故障恢复适用场景
    主从复制 读写分离、数据冗余 1主N从 手动切换 读多写少,允许短时不可用
    哨兵模式 自动故障转移 1主N从 + 哨兵集群 自动(秒级) 需要高可用,数据量不大
    Cluster集群 数据分片 + 水平扩展 N主N从 自动 + 数据迁移 大数据量、高吞吐量

    4.2 主从复制原理(深度)

    (1)全量复制流程

    Slave 与 Master 首次建立连接,执行完整数据同步:

    Slave 发送 PSYNC 命令 → Master 触发 BGSAVE 生成 RDB → 传输 RDB 文件 → Slave 加载数据 → 同步积压增量命令

    关键配置:

    repl-diskless-sync yes # 无盘复制(直接通过网络发送RDB)
    repl-backlog-size 64mb # 复制积压缓冲区大小(影响增量复制能力)

    (2)增量复制(断线重连)

    当Slave短暂断开后重连,发送PSYNC runid offset:

    • 如果offset仍在Master的repl_backlog中 → 增量同步(只同步缺失的命令)

    • 如果offset已不在repl_backlog中 → 退化为全量同步

    repl_backlog大小计算公式:

    repl_backlog_size = 断线重连预期最大时间 × 主节点写入命令产生数据量/秒


    4.3 哨兵机制(Sentinel)深度解析

    (1)三大任务
    任务说明实现方式
    监控 检查Master和Slave是否正常 每秒发送PING,判断响应时间
    通知 发生问题时通知管理员/其他程序 通过pub/sub或API
    自动故障转移 Master宕机后选举新Master 投票+选举,完成主从切换
    (2)主观下线 vs 客观下线
    状态判定条件说明
    主观下线(SDOWN) 单个哨兵认为Master下线 超过down-after-milliseconds未响应PING
    客观下线(ODOWN) 多数哨兵(quorum)认为Master下线 达到后触发故障转移流程
    (3)选举算法

    当一个哨兵执行故障转移时,从Slave中选举新Master,优先级:

  • 断开时长:与Master断开越久的Slave优先级越低

  • slave-priority:配置文件中的优先级(数字越小越优先)

  • 复制偏移量:复制数据越多的Slave越优先

  • runid:若以上都相同,选runid最小的

  • (4)数据丢失问题与解决方案

    两种数据丢失场景:

  • 异步复制丢失:Master写入后,数据还未同步到Slave,Master宕机

  • 脑裂丢失:网络分区导致双Master,分区恢复后冲突数据被覆盖

  • 解决方案(redis.conf):

    min-replicas-to-write 1 # Master至少要有1个从节点才能写入
    min-replicas-max-lag 10 # 从节点最大延迟不超过10秒

    注意:此配置会降低可用性换取数据安全,需权衡


    4.4 Redis Cluster集群(深度)

    (1)核心设计理念
    特性说明
    无中心节点 节点间通过Gossip协议交换状态,没有代理层
    哈希槽 16384个slot,CRC16(key) % 16384决定归属
    客户端直连 节点返回MOVED重定向,客户端直接连接目标节点
    自动故障转移 主节点宕机后,从节点被选举为新主
    (2)哈希槽分布

    CRC16(key) % 16384 = slot

    为什么是16384个槽?

    • 集群中节点数通常不超过1000,16384足够

    • 节点间通过gossip传播槽信息,16384个槽占用的bitmap约为2KB(16384/8/1024),传输效率高

    • 早期版本使用4096,后来增加到16384

    (3)Gossip协议

    节点间每秒随机选择几个节点发送PING消息,交换状态信息:

    消息类型内容
    PING 发送节点的状态 + 随机几个其他节点的状态
    PONG 对PING的响应
    MEET 将新节点加入集群
    FAIL 广播节点宕机信息
    (4)MOVED重定向 vs ASK重定向
    响应场景客户端行为
    MOVED key的slot永久归属另一个节点 更新本地slot映射缓存,重试
    ASK slot正在迁移中 仅本次重定向到目标节点,不更新缓存
    (5)集群扩缩容

    扩容流程:

  • 启动新节点,加入集群:CLUSTER MEET <ip> <port>

  • 为新节点分配槽位(迁移):CLUSTER SETSLOT <slot> IMPORTING/NODE

  • 数据迁移使用MIGRATE命令(原子操作)

  • 在线迁移对业务影响:

    迁移过程中,相关key可能收到ASK重定向,建议在低峰期进行,或分批迁移

    五、性能优化与问题排查篇

    5.1 缓存三大问题:穿透、击穿、雪崩(必考)

    问题现象核心原因解决方案
    缓存穿透 查询不存在的数据直打数据库 恶意请求/数据不存在 ①布隆过滤器预过滤 ②缓存空对象(短TTL) ③参数校验
    缓存击穿 热点Key过期瞬间打崩数据库 高并发 + 热点Key同时过期 ①互斥锁(SETNX) ②逻辑过期(不设TTL,异步更新)
    缓存雪崩 大量Key同时过期或Redis宕机 同一时间大批Key过期 / Redis故障 ①随机TTL ②多级缓存 ③高可用集群 ④限流降级
    深入:布隆过滤器原理

    # 布隆过滤器特点
    空间效率高:100万数据仅占约1MB
    可能误判:判断"不存在"一定准确,"存在"可能误判
    不支持删除(除非使用Counting Bloom Filter)

    # 常用实现
    Redisson的RBloomFilter、Google Guava的BloomFilter

    深入:互斥锁解决击穿的伪代码

    def get_data(key):
    value = redis.get(key)
    if value is not None:
    return value

    # 尝试获取锁
    if redis.setnx("lock:" + key, "1", ex=10):
    value = db.query(key)
    redis.set(key, value, ex=3600)
    redis.delete("lock:" + key)
    return value
    else:
    # 等待其他线程加载
    time.sleep(0.1)
    return redis.get(key) # 重试


    5.2 缓存与数据库双写一致性(必考)

    业界最佳实践:Cache Aside Pattern(旁路缓存模式)

    操作流程原因
    先读缓存 → 命中返回 → 未命中读DB → 写入缓存 保证缓存存在
    先更新DB → 再删除缓存(不是更新!) 避免复杂计算和并发问题
    追问1:为什么是删除缓存而不是更新缓存?

    更新成本高(可能需复杂计算/聚合),且若写多读少,频繁更新却没人读,浪费性能。删除缓存后,下次读时再懒加载,更高效。

    追问2:删除缓存失败怎么办?

    方案一:可靠MQ重试

    更新DB → 发送删除缓存消息 → 消费者删除缓存,失败进入重试队列(最多5次)

    方案二:Canal监听MySQL binlog(大厂主流)

    MySQL binlog → Canal解析 → 消息队列 → 消费者删除缓存

    优点:解耦,不影响主业务流程;缺点:架构复杂,增加延迟

    追问3:延时双删是什么?

    # 延时双删流程
    redis.delete(cache_key) # 第一次删除
    db.update(data) # 更新数据库
    time.sleep(500) # 休眠500ms
    redis.delete(cache_key) # 第二次删除

    为什么需要第二次删除?

    在"删缓存→更新DB"期间,可能有其他线程读取了DB旧数据并写入缓存。第二次删除是为了清除这个"脏缓存"。休眠时间要大于可能发生的读操作耗时。


    5.3 分布式锁实现(必考)

    正确实现(Redis 2.6.12+):

    SET lock_key unique_value NX PX 30000

    • NX:键不存在时设置(互斥)

    • PX:过期时间(防死锁)

    • unique_value:通常是UUID/线程ID,用于释放时验证身份

    释放锁的Lua脚本(保证原子性):

    if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
    else
    return 0
    end

    核心问题与解决方案:

    问题解决方案
    死锁 必须设置过期时间(PX参数)
    误删他人锁 释放前判断value是否为自己(Lua脚本)
    锁过期业务未完成 看门狗(Watch Dog)自动续期(Redisson实现)
    集群下锁丢失 Redlock算法(向多数节点申请锁)
    Redisson可重入锁实现原理

    使用Hash结构存储锁信息:

    # 加锁
    hset mylock uuid:threadId 1
    pexpire mylock 30000

    # 重入(再次加锁)
    hincrby mylock uuid:threadId 1 # value+1

    # 释放锁
    hincrby mylock uuid:threadId 1
    if value <= 0:
    del mylock

    看门狗机制:

    默认每10秒检查一次锁是否存在,若锁还存在且业务未完成,自动续期30秒,避免了锁过期导致业务中断

    Redlock算法的争议

    Martin Kleppmann(《数据密集型应用系统设计》作者)与Redis作者Antirez针对Redlock算法存在经典争议,核心围绕算法安全性、场景适配性展开,是高频面试延伸考点:

    1. 争议核心:Redlock是否真的安全?

    Martin 反方观点(质疑Redlock):

    Redlock算法无法解决分布式系统中的时钟漂移、网络延迟、进程暂停问题,不具备真正的一致性保障。分布式锁的核心依赖全局统一时间,而真实分布式环境中,服务器系统时间无法完全同步,会出现以下问题:

    • 节点时间漂移:部分节点时钟过快/过慢,导致锁过期时间判定不一致

    • GC暂停:业务进程GC卡顿、系统阻塞,导致锁持有超时但业务未执行完

    • 网络延迟:锁请求、响应超时,造成锁判定失效

    因此Martin认为:Redlock设计存在缺陷,无法保证分布式锁的互斥性。

    Antirez 正方观点(Redis作者辩护):

    Redlock的设计是工程最优解,而非理论完美解,适配Redis的业务定位:

    • Redis节点本地时钟偏差极小,线上服务器会开启时间同步服务,时钟漂移可忽略

    • GC、进程暂停属于系统级异常,并非锁算法本身的问题,所有分布式锁都会面临该问题

    • Redlock牺牲了极致理论一致性,换取了极高的性能和可用性,适配缓存、业务锁场景

    2. 面试标准答案:Redlock适用场景取舍

    不适用场景(绝对禁止使用):金融交易、资金结算、核心账务等强一致性、零数据丢失场景,此类场景必须使用Zookeeper、etcd等基于CP模型的分布式锁。

    适用场景(推荐使用):业务限流、秒杀抢单、接口幂等、任务调度等高可用、高性能优先,允许极小概率异常的场景。

    3. 企业主流落地方案

    绝大多数互联网公司线上生产环境:单节点Redis分布式锁 + 兜底降级,而非Redlock。原因如下:

    • Redlock需要部署5个独立Redis节点,部署成本高、运维复杂

    • 多节点请求耗时更高,性能远低于单节点锁

    • 业务中极少需要极致锁一致性,单节点配合高可用集群(哨兵/Cluster)足以满足99.99%场景


    5.4 Redis热点Key、大Key问题及优化(生产高频)

    (1)大Key定义与危害

    大Key判定标准:

    • String类型:value大小 > 10KB

    • List/Set/Hash/ZSet:元素数量 > 1000个

    核心危害:

    • 读写耗时过长,阻塞Redis主线程,引发命令超时

    • 网络传输量大,占用带宽,导致集群卡顿

    • 集群迁移、过期删除、内存淘汰时耗时剧增,触发服务抖动

    (2)大Key优化方案
    • 拆分Key:将大Hash/List拆分为多个小Key,例如user:info:{id}拆分为多个独立字段Key

    • 精简数据:剔除无效冗余数据,缩短value长度,序列化使用更紧凑的协议(Protobuf替代JSON)

    • 分批操作:使用HSCAN、SSCAN、ZSCAN渐进式遍历,避免一次性全量读取

    • 异步删除:开启Redis懒删除配置lazyfree-lazy-user-del yes,大Key删除不阻塞主线程

    (3)热点Key问题与解决方案

    热点Key定义:QPS极高的核心Key(如秒杀商品、首页热点数据),单节点流量打爆,引发性能瓶颈。

    解决方案:

    • 本地缓存多级降级:JVM本地缓存 + Redis缓存,减少Redis直接访问压力

    • Key分片:将一个热点Key拆分为多个分片Key,分摊流量压力

    • 集群负载均衡:利用Redis Cluster哈希槽分散热点数据

    • 限流熔断:接口层限流,拦截无效高频请求


    六、Redis经典面试压轴题

    6.1 Redis为什么不适合存储大量冷数据?

  • 内存成本极高:Redis基于内存存储,内存硬件成本远高于磁盘,大量冷数据常驻内存会造成资源严重浪费;

  • 触发频繁内存淘汰:冷数据占用内存,会导致热点数据被淘汰,降低缓存命中率,影响业务性能;

  • 持久化开销大:大量冷数据会增大RDB/AOF文件体积,fork、重写、刷盘耗时大幅增加,拖慢Redis主线程;

  • 数据冗余低效:冷数据无访问价值,无需常驻内存,可落地至MySQL、ES等磁盘存储数据库。

  • 6.2 Redis单线程为什么能支撑10万+ QPS?

    结合前文核心知识点总结,面试标准满分回答:

    Redis超高QPS的核心是无锁单线程 + 非阻塞I/O + 高效底层设计的组合优势:

  • 核心命令单线程执行,无多线程上下文切换、锁竞争开销,CPU利用率极致;

  • 基于epoll I/O多路复用,单线程可监听数万socket连接,实现非阻塞事件驱动;

  • 纯内存读写,避开磁盘IO低速瓶颈,读写耗时为纳秒级;

  • 底层SDS、跳表、quicklist等定制化高效数据结构,时间复杂度极低;

  • Redis 6.0+将网络收发包、协议解析改为多线程,进一步释放并发能力,核心命令仍保持单线程保证安全。

  • 6.3 Redis集群最大能支持多少节点?为什么?

    官方上限:16384个节点,生产建议不超过1000个

    核心原因:

  • Redis Cluster固定16384个哈希槽,槽位数量决定集群最大节点容量;

  • 集群通过Gossip协议同步节点状态,节点过多会导致集群心跳、状态同步消息暴涨,网络带宽被大量占用;

  • 节点越多,槽位分配、故障转移、数据迁移的复杂度指数级上升,集群稳定性大幅下降;

  • 16384个槽位的bitmap仅2KB,适配Gossip协议高效传输,是官方权衡性能与容量的最优设计。

  • 6.4 Redis宕机快速恢复方案(生产实战)

  • 临时应急:开启哨兵自动故障转移,秒级切换主节点,恢复业务读写;

  • 数据恢复:优先加载混合持久化文件,RDB快速恢复全量数据+AOF补全增量数据;

  • 流量兜底:临时开启本地缓存、限流降级,避免大量请求打崩数据库;

  • 事后复盘:排查宕机原因(内存溢出、大Key阻塞、fork耗时过长、网络波动),优化配置与业务代码。

  • 终极生产架构:Redis Cluster集群 + 哨兵兜底 + 混合持久化 + 本地多级缓存,实现高可用、高性能、高容错的整体架构。


    七、Redis场景化面试实战真题(高频必考)

    本章节聚焦真实业务场景面试题,摒弃纯理论提问,覆盖秒杀、限流、队列、用户体系、数据统计、线上故障排查等高频面试场景,是区分初级、中级开发的核心考点。

    7.1 秒杀场景Redis实战方案(电商高频)

    面试提问:如何用Redis实现高并发秒杀?如何解决秒杀超卖、并发冲突问题?

    整体架构思路:采用Redis预减库存 + 接口限流 + 异步下单,将秒杀压力拦截在数据库之外,支撑万级并发。

    完整实现流程:

  • 预热阶段:秒杀开始前,将商品库存数量批量写入Redis,设置过期时间(对应秒杀活动时长);

  • 请求拦截:接口层做IP限流、用户维度限流,拦截重复请求、恶意刷量请求;

  • 原子扣库存:通过Lua脚本保证「判断库存-扣减库存」原子执行,杜绝超卖;

  • 异步下单:Redis库存扣减成功后,将用户订单信息推送至MQ,消费者异步创建数据库订单;

  • 结果兜底:秒杀结束后,同步Redis剩余库存至数据库,定时清理无效订单。

  • 核心防超卖Lua脚本:

    — key:商品库存key,arg1:扣减数量
    local stock = tonumber(redis.call("get", KEYS[1]) or 0)
    if stock >= tonumber(ARGV[1]) then
    — 库存充足,扣减并返回成功
    redis.call("decrby", KEYS[1], ARGV[1])
    return 1
    end
    — 库存不足,返回失败
    return 0

    核心坑点解决:

    • 超卖问题:Lua脚本保证原子性,禁止分步查询+扣减;

    • 少卖问题:禁止库存预扣减过量,结合用户限购策略;

    • 恶意刷单:新增用户唯一标识限流、单次活动限购次数限制。

    7.2 接口幂等性Redis实现(业务通用)</h4 id=“903”>面试提问:什么是接口幂等?如何用Redis实现支付、回调、重复提交的幂等控制?幂等定义:同一请求多次重复提交,最终业务结果一致,不会产生脏数据、重复订单、重复扣款等问题。Redis实现方案(业界主流):1. 唯一键约束:利用业务唯一标识(订单号、支付流水号、请求token)作为Redis Key;2. 原子防重:通过SET key value NX PX 过期时间实现幂等控制;3. 过期适配:Key过期时间大于业务最大执行时长,避免业务未完成、幂等失效。执行流程:请求进来,先根据唯一标识尝试设置Redis Key;设置成功:首次请求,执行业务逻辑;设置失败:重复请求,直接返回成功,不重复执行业务。场景适配:支付回调、表单重复提交、定时任务重复执行、接口重试机制。7.3 流量限流&防刷场景(网关/接口高频)

    面试提问:如何用Redis实现IP限流、用户限流、接口限流?滑动窗口限流怎么实现?

    1. 简单固定窗口限流(入门)

    以「IP+接口」为Key,固定时间窗口(1分钟)统计请求次数,超过阈值直接拦截。优点实现简单,缺点存在临界突刺问题(窗口交界处流量翻倍)。

    2. Redis滑动窗口限流(生产最优)

    利用ZSet有序集合实现,核心思路:

    • Key为限流标识(IP/用户ID),Score和Value均为当前时间戳;

    • 移除窗口外的过期请求数据;

    • 统计当前窗口内请求总数,判断是否超过限流阈值;

    • 新增当前请求时间戳数据。

    优势:流量分布均匀,彻底解决固定窗口临界突刺问题,适配高并发网关限流。

    7.4 消息队列场景落地对比(Redis实操)

    面试提问:Redis哪些数据结构可以做消息队列?各自优缺点?为什么不推荐List做可靠消息队列?

    1. List实现简单队列(LPUSH+RPOP)

    优点:实现简单、性能极高;缺点:无消息确认、无消费组、消息丢失、无法重试,仅适用于非可靠、低优先级消息。

    2. Stream实现可靠消息队列(生产推荐)

    Redis 5.0+推出的Stream类型,具备完整消息队列特性:

    • 支持消息持久化、消息ACK确认;

    • 支持消费组、消息重试、死信队列;

    • 支持断点续读,有效避免消息丢失。

    面试总结:临时日志、简单通知可用List;订单消息、业务异步处理、可靠通知必须用Stream,不建议用Redis做核心交易队列,核心业务优先RocketMQ/Kafka。

    7.5 用户签到&活跃度统计场景

    面试提问:如何用Redis高效实现用户每日签到、连续签到、月签到统计?

    最优方案:Bitmap位图

    核心设计:以「用户ID+年月」为Key,一个bit位对应一天的签到状态(0未签到、1已签到),极大节省内存。

    实操命令:

    • 签到:SETBIT user:sign:1001:202606 5 1(当月第5天签到);

    • 查询当日签到:GETBIT user:sign:1001:202606 5;

    • 统计月签到次数:BITCOUNT user:sign:1001:202606;

    • 查询连续签到:遍历bit位统计连续1的最大长度。

    优势:一个用户每月签到数据仅占用32字节左右,百万用户内存占用极低,读写性能O(1)。

    7.6 海量UV/PV统计场景

    面试提问:如何用Redis统计页面UV(独立访客),千万级数据如何低成本实现?

    精准统计(小数据量):使用Set集合,用户访问时SADD用户ID,SCARD统计总数,数据精准但内存占用高。

    模糊统计(大数据量、生产主流):使用HyperLogLog

    核心特性:12KB内存可统计上亿级UV,误差率极低(0.81%左右),完全满足运营统计需求。

    实操命令:

    • 记录访客:PFADD page:uv:20260605 user1001 user1002;

    • 统计UV:PFCOUNT page:uv:20260605;

    • 合并多日UV:PFMERGE。

    面试取舍:精准数据用Set,运营统计、海量数据优先HyperLogLog。

    7.7 排行榜场景实战设计

    面试提问:电商销量榜、积分排行榜、实时排名如何用Redis实现?

    核心方案:ZSet有序集合

    设计规则:Key为排行榜类型,Member为用户/商品ID,Score为排序权重(销量、积分、分数)。

    核心操作:

    • 更新排名:ZINCRBY 累加权重分数;

    • 查询TOP10:ZREVRANGE 倒序查询(从高到低);

    • 查询个人排名:ZREVRANK。

    进阶优化:海量数据排行榜可做冷热分离,实时热点数据存ZSet,历史榜单持久化到MySQL,定时异步更新。

    7.8 线上Redis故障场景面试题(复盘类)

    1. 面试提问:线上Redis突然CPU飙升100%,如何排查?

    常见原因:大Key读写阻塞主线程、存在热Key无限请求、使用KEYS/FLUSHALL等高危命令、大量fork子进程、缓存击穿瞬时流量暴涨。

    排查步骤:

    • 执行INFO stats查看命令执行次数、QPS波动;

    • 执行INFO memory查看内存占用、大Key情况;

    • 开启慢查询日志,定位耗时最长的命令;

    • 排查是否存在热点Key、过期Key集中失效问题。

    2. 面试提问:Redis缓存命中率极低,可能是什么原因?如何优化?

    核心原因:

    • Key过期时间过短,数据频繁失效;

    • 内存淘汰策略不合理,热点数据被淘汰;

    • 大量冷数据常驻内存,占用资源;

    • 存在大量缓存穿透,空缓存频繁刷新。

    优化方案:合理设置TTL、采用allkeys-lru淘汰策略、清理冷数据、布隆过滤器防穿透、热点数据永久缓存(异步更新)。

    3. 面试提问:Redis集群数据迁移后,出现数据丢失怎么办?

    大概率是槽位迁移不完整、迁移期间节点宕机、ASK重定向异常导致。解决方案:停止迁移、校验槽位数据完整性、通过RDB文件恢复数据、重新均衡槽位,低峰期分批迁移规避问题。

    7.9 延时队列场景实现

    面试提问:如何用Redis实现延时队列?(订单超时取消、延时通知场景)

    最优方案:ZSet实现延时队列

    设计思路:Member存储任务唯一标识和任务参数,Score存储任务执行时间戳。

    执行流程:

    • 新增延时任务:ZADD delay:queue 执行时间戳 任务信息;

    • 定时轮询:客户端定时ZRANGEBYSCORE查询已到执行时间的任务;

    • 原子抢占任务:通过ZREM删除任务,成功则执行业务,避免重复执行。

    适用场景:订单30分钟未支付自动取消、消息延时推送、定时任务延迟执行。


    八、Redis 超全拓展场景面试题(大厂高频|新增海量真题)

    本节为独家补充场景题库,网上八股极少覆盖,是大厂二面/三面高频实操题,全部为生产真实问题。

    8.1 缓存穿透专项场景题

    场景1:用户恶意刷不存在用户ID,数据库CPU打满,如何处理?

    标准回答:

  • 常规空缓存方案:查询不存在数据,缓存空值并设置短TTL(30s),避免频繁穿透;

  • 恶意攻击优化:空值缓存单独标记,配合IP限流、黑名单拦截;

  • 海量数据场景:部署布隆过滤器,预加载合法用户ID,非法ID直接拦截,不查缓存和DB;

  • 参数校验层前置:ID格式非法、负数、超长直接拦截。

  • 场景2:布隆过滤器误判导致业务问题,如何解决?
  • 误判只会出现「不存在判定为存在」,不会出现「存在判定为不存在」,不影响数据一致性;

  • 调高bit数组大小、降低误判率;

  • 定时重建布隆过滤器,解决数据新增不同步问题;

  • 兜底:穿透少量请求不影响整体性能。

  • 8.2 缓存击穿深度场景题

    场景1:热点Key凌晨统一过期,瞬间万级QPS打崩DB,如何根治?
  • 热点Key禁止固定TTL,采用逻辑过期:缓存永久有效,后台异步更新;

  • 若必须TTL,加随机抖动时间 ±30s,打散过期时间;

  • 分布式互斥锁+热点Key限流,单节点只放行一个线程更新缓存;

  • 热点数据多级缓存:本地缓存兜底,杜绝全部请求落Redis。

  • 场景2:Redisson看门狗续期失效导致业务中断,原因?
  • 业务执行时间过长、线程阻塞;

  • 客户端断连、网络抖动,续期线程无法上报;

  • 解决:关键业务拆分短任务、增加超时监控、增加重试机制。

  • 8.3 缓存雪崩高阶场景题

    场景1:Redis整集群宕机,如何保证业务不崩?
  • 本地缓存(Caffeine/Guava)兜底,优先读取本地;

  • 网关层限流、熔断,降级返回默认值;

  • 禁止所有请求穿透DB,做QPS阈值拦截;

  • Redis恢复后,异步预热重建缓存,避免瞬间流量冲击。

  • 场景2:大批量key同时过期,除了随机TTL还有什么方案?
  • 批量数据分批预热写入,错开过期时间;

  • 冷热数据分离,热点数据永久缓存;

  • 后台定时轮刷热点缓存,主动更新,不依赖过期触发;

  • 集群分片打散过期key。

  • 8.4 缓存一致性高频场景

    场景1:高并发读写场景,出现缓存脏数据如何彻底解决?
  • 严格遵守:更新DB→删除缓存,禁止更新缓存;

  • 高并发读写冲突,增加延时双删机制;

  • 极高频热点数据,使用订阅binlog异步删缓存最终一致;

  • 短时间脏数据可容忍,互联网业务基本最终一致即可。

  • 场景2:为什么大厂基本不用「先删缓存、再更新DB」?

    会出现严重脏数据:

  • 线程A删缓存 → 线程B查DB旧数据 → 写入旧缓存 → 线程A更新DB

  • 最终缓存永久脏数据,无法自动恢复;

  • 因此工业级规范:先库后删。

  • 场景3:删除缓存失败,线上数据不一致如何兜底?
  • 消息重试机制:删除失败送入MQ重试队列;

  • Canal监听binlog,异步兜底删除;

  • 定时巡检任务,对比DB与缓存差异,自动修复脏数据。

  • 8.5 大Key、热Key线上真实场景

    场景1:一个大Hash Key导致Redis卡顿、接口超时,如何紧急止血?

    紧急方案:

  • 立刻开启懒删除 lazyfree-lazy-user-del yes

  • 禁止一次性hgetall,改用hscan分批读取

  • 业务层临时降级,限制该Key访问QPS

  • 根治方案:Hash字段拆分多Key、数据精简、冷热分离。

    场景2:秒杀热Key导致单节点打爆,集群无法分摊怎么办?
  • 热点Key分片:stock_1、stock_2、stock_3 多key分摊流量;

  • 本地缓存兜底,拦截90%重复流量;

  • 接口层限流削峰;

  • 秒杀库存预加载多分片,均匀打散槽位。

  • 8.6 分布式锁疑难场景

    场景1:单机锁没问题,上集群后出现锁失效、并发超卖为什么?
  • 主从异步复制,主节点加锁成功、未同步从节点就宕机;

  • 从节点升级为主节点,锁数据丢失,导致锁失效;

  • 解决:

  • 金融级使用Redlock;

  • 普通业务使用Redisson集群锁;

  • 关键业务加数据库乐观锁兜底。

  • 场景2:锁过期时间到底设多少?业务时间不固定怎么办?
  • 固定过期时间无法适配动态业务;

  • 生产标准:不自己设过期时间,统一用Redisson看门狗;

  • 自动续期,业务结束主动释放,彻底解决锁过期问题。

  • 8.7 Redis集群线上故障场景(超高频面试)

    场景1:Cluster集群迁移槽位,业务报ASK错误怎么办?
  • ASK是正常迁移中间状态,代表槽位正在迁移;

  • 客户端需跟随重定向访问目标节点;

  • 低峰期迁移、分批迁移,避免大批量ASK报错影响用户。

  • 场景2:集群脑裂产生双主,数据覆盖丢失如何避免?
  • 开启防护配置:min-replicas-to-write、min-replicas-max-lag

  • 网络分区后主节点无足够从节点,直接禁止写入;

  • 集群多数派机制保证数据安全。

  • 8.8 消息队列业务场景难题

    场景1:Redis Stream消息丢失、重复消费如何解决?
  • 开启ACK确认机制,未ACK消息自动重试;

  • 设置消息pending超时,死信消息转入死信队列;

  • 消费端保证幂等,防止重复消费异常;

  • 合理配置maxlen截断,避免消息堆积溢出。

  • 场景2:为什么大厂不用Redis做核心消息队列?
  • Redis不保证消息绝对不丢(极端宕机、持久化未刷盘会丢);

  • 无完整事务、重试、死信、堆积监控体系;

  • 高堆积会拖垮Redis性能,影响缓存核心业务;

  • 核心交易必须使用专业MQ。

  • 8.9 统计类高阶场景题

    场景1:如何统计每日在线用户、七日留存?
  • 每日在线:Bitmap按日记录用户在线状态;

  • 七日留存:连续七日Bitmap做与运算,统计留存人数;

  • 优势:亿级用户仅几十MB内存,远超MySQL性能。

  • 场景2:如何实现滑动窗口UV统计?

    使用HyperLogLog + 定时归并,每日滚动合并最近7天数据,实现滑动窗口独立访客统计。

    8.10 限流高阶场景

    场景1:如何实现用户维度+IP维度+接口维度多层限流?
  • 单IP一分钟100次拦截;

  • 单用户一分钟50次拦截;

  • 全接口全局总QPS限流;

  • 三层拦截层层兜底,防刷、防雪崩。

  • 场景2:滑动窗口限流为什么比令牌桶、漏桶更适合Redis?
  • Redis是统计型中间件,天然适合时间窗口统计;

  • ZSet实现简单、无复杂算法、分布式一致性好;

  • 规避临界流量突刺,适配互联网突发流量。

  • 8.11 超经典冷门但必考场景题

    1、Redis为什么不能存超大单Key?

    单Key过大是线上90% Redis故障的核心根源,会引发一系列严重问题:导致Redis主线程阻塞、集群数据迁移卡顿、过期Key删除耗时激增、服务器网络IO暴涨,严重影响整体服务稳定性,因此业务中必须严格规避超大单Key。

    2、Redis持久化会影响业务性能吗?如何规避?

    会对业务性能产生一定影响,持久化的fork子进程、AOF日志刷盘、AOF文件重写等操作,都会占用服务器CPU、内存与磁盘资源。

    规避方案:选择业务低峰期执行RDB备份、控制单Redis实例内存大小、使用SSD高速存储介质、关闭非必要的文件压缩配置,最大限度降低持久化对业务的性能损耗。

    3、Redis过期Key太多导致CPU高如何处理?

    可通过四大方案优化解决:①开启Redis懒删除配置,大体积过期Key删除不阻塞主线程;②为Key过期时间添加随机抖动,分散过期时间,避免批量过期;③新增定时任务,主动清理长期无效的过期Key;④优化内存淘汰策略,适配业务冷热数据场景,减少过期Key堆积。

    4、如何实现Redis读写分离?业务怎么适配?

    核心实现方式:搭建Redis主从架构,主节点专门负责写操作,从节点承担所有读请求,实现读写分离、流量拆分。

    业务适配方案:客户端做读写请求路由,写请求统一指向主节点,读请求分发至从节点;该方案能大幅提升高读低写业务的整体吞吐能力,缓解主节点访问压力。

    5、Redis冷热数据分离怎么做?

    主流落地方案:热点核心数据常驻Redis内存,保障读写性能;长期无访问的冷数据定时迁移至MySQL、ES等磁盘存储中间件;定时清理Redis中过期、无用的冷数据Key,避免冷数据长期占用内存,造成资源浪费,提升缓存命中率。

    赞(0)
    未经允许不得转载:171主机测评 » 吃透这篇 Redis 面试题,搞定 90% 后端面试(2026 超全整理)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址