Redis

Redis 是面试中高频出现的中间件,涉及基础数据结构、持久化、高可用、缓存问题等多个维度。以下是常见面试题及核心要点:

一、基础概念与特性

  1. 什么是 Redis?它有哪些核心特性?

    • 定义:Redis 是开源的高性能键值对内存数据库,支持多种数据结构,可用于缓存、分布式锁、消息队列等场景。
    • 核心特性:
      • 基于内存操作,速度快(毫秒级响应);
      • 支持丰富的数据结构(String、Hash、List、Set、Sorted Set 等);
      • 支持持久化(RDB、AOF),避免内存数据丢失;
      • 支持主从复制、哨兵、集群,保证高可用;
      • 单线程模型(核心逻辑单线程,避免线程切换开销);
      • 支持 Lua 脚本、事务、发布订阅等。
  2. Redis 为什么这么快?

    • 内存存储:数据存在内存中,避免磁盘 IO 开销(磁盘 IO 是毫秒级,内存是纳秒级)。
    • 单线程模型:核心读写逻辑用单线程,避免多线程切换和锁竞争(Redis 6.0 后 IO 线程可并行处理网络请求,但核心逻辑仍单线程)。
    • 高效数据结构:针对不同场景优化数据结构(如 String 用 SDS 动态字符串,Hash 用哈希表 + 压缩列表)。
    • IO 多路复用:用 epoll/kqueue 等 IO 多路复用模型,单线程处理多个客户端连接,减少阻塞。
  3. Redis 支持哪些数据结构?各自的应用场景是什么?

    数据结构 底层实现(简化) 核心特性 应用场景
    String(字符串) 简单动态字符串(SDS) 二进制安全,支持拼接、自增 / 减 缓存(如用户信息)、计数器、分布式 ID
    Hash(哈希) 哈希表 + 压缩列表(小数据) 键值对集合,适合存储对象 存储用户信息(name/age/addr)
    List(列表) 双向链表 + 压缩列表(小数据) 有序,可重复,支持两端操作 消息队列(lpush + rpop)、最新列表
    Set(集合) 哈希表 + 整数集合(小整数) 无序,不可重复,支持交集 / 并集 好友关系(共同好友)、去重
    Sorted Set(有序集合) 跳表 + 哈希表 有序(按 score 排序),不可重复 排行榜(如销量排名)、延时队列
  4. Redis 的单线程模型是怎样的?为什么单线程还能处理高并发?

    • 单线程模型:Redis 核心的 "网络 IO" 和 "数据操作" 逻辑由一个主线程处理,避免多线程的上下文切换和锁开销。
    • 高并发原因:
      • 内存操作速度极快,单线程足以处理大部分请求;
      • 用 IO 多路复用(如 epoll)同时监听多个客户端连接,主线程通过 "事件循环" 处理就绪的 IO 事件(读 / 写),无需阻塞等待。
    • 注意:Redis 6.0 引入 "多 IO 线程",仅负责网络数据的读写(解析命令、返回结果),核心的数据处理仍单线程,进一步提升并发能力。

二、持久化机制

  1. **Redis 的持久化机制有哪些?(RDB vs AOF)**持久化用于将内存数据写入磁盘,避免宕机丢失,两种核心方式:

    维度 RDB(快照) AOF( Append Only File)
    原理 定时生成内存全量数据的二进制快照(dump.rdb 记录所有写命令到日志文件(appendonly.aof),恢复时重放命令
    触发方式 手动(save/bgsave)、自动(配置 save <秒> <次数> 实时追加(依赖 appendfsync 策略)
    优点 文件小,恢复速度快;适合备份 数据丢失少(最多丢失 1 秒);命令可读
    缺点 快照间隔内数据可能丢失;bgsave fork 子进程耗资源 文件大,恢复慢;写命令追加可能影响性能
    适用场景 数据允许短期丢失,需快速恢复 数据安全性要求高(如金融场景)
  2. RDB 的 savebgsave 有什么区别?

    • save:主线程执行,会阻塞 Redis 服务(期间无法处理客户端请求),适合停机备份。
    • bgsave:主线程 fork 一个子进程负责生成 RDB 文件,主线程继续处理请求(非阻塞),是默认推荐方式。
    • 注意:fork 子进程时会复制内存页表(写时复制),若内存大,fork 可能短暂阻塞主线程。
  3. AOF 的重写机制是什么?为什么需要重写?

    • 原因:AOF 文件会因重复命令(如多次 set key value)越来越大,导致恢复慢、占用磁盘空间。
    • 重写机制:通过 bgrewriteaof 命令(或自动触发),生成一个 "精简版" AOF 文件 ------ 直接记录数据的最终状态(如将 set a 1; set a 2 合并为 set a 2)。
    • 流程:主线程 fork 子进程,子进程遍历内存数据生成新 AOF 命令;重写期间新命令写入 "重写缓冲区",完成后追加到新文件,替换旧文件。
  4. Redis 4.0 后的混合持久化是什么?

    • 混合持久化:RDB 作为 AOF 文件的开头(存储全量数据快照),后续追加 AOF 增量命令。
    • 优势:结合 RDB 恢复快和 AOF 数据全的优点 ------ 恢复时先加载 RDB 基础数据,再执行 AOF 增量命令,兼顾速度和安全性。

三、高可用与分布式

  1. Redis 主从复制的原理是什么?

    • 主从复制:通过复制将主节点(Master)的数据同步到从节点(Slave),实现读写分离(Master 写,Slave 读)和数据备份。
    • 核心流程:
      1. 从节点连接主节点,发送 SYNC 命令(Redis 2.8 前)或 PSYNC 命令(支持部分同步)。
      2. 主节点 bgsave 生成 RDB 文件,发送给从节点;从节点清空旧数据,加载 RDB。
      3. 主节点将 RDB 生成期间的写命令记录到 "复制缓冲区",发送给从节点;从节点执行这些命令,与主节点数据一致。
      4. 后续主节点的写命令会实时同步到从节点(增量复制)。
  2. 哨兵(Sentinel)的作用是什么?工作流程是怎样的?

    • 作用:监控主从节点状态,当主节点宕机时自动将从节点升级为新主节点(故障转移),保证 Redis 高可用。
    • 工作流程:
      1. 监控 :哨兵定期向主从节点发送 PING 命令,判断节点是否存活(主观下线)。
      2. 判断:若主节点主观下线,哨兵集群投票(超过半数同意),标记为主节点 "客观下线"。
      3. 故障转移
        • 从哨兵中选举一个 "领导者" 负责处理故障转移。
        • 从所有从节点中选一个 "最优从节点"(如数据最新、优先级高)作为新主节点。
        • 让其他从节点复制新主节点,原主节点恢复后作为从节点。
  3. Redis 集群(Cluster)的架构是什么?如何实现分片?

    • 架构:Redis 集群是分布式存储方案,由多个主从节点组成(每个主节点可带从节点),解决单节点容量和性能瓶颈。
    • 分片机制:
      • 集群将数据分为 16384 个 "槽位(slot)",每个主节点负责一部分槽位(如 3 主节点,各负责~5461 个槽)。
      • 数据存储时,通过 CRC16(key) % 16384 计算 key 所属槽位,再路由到负责该槽位的主节点。
    • 高可用:主节点宕机后,其从节点会升级为新主节点,接管槽位。
  4. Redis 集群如何处理槽位迁移?

    • 槽位迁移:当集群扩容 / 缩容时,需要将槽位从旧主节点迁移到新主节点,保证数据均衡。
    • 流程(简化):
      1. 源节点将槽位中的 key 逐个迁移到目标节点(先迁移,再更新槽位映射)。
      2. 迁移期间,客户端访问该槽位的 key 时,源节点会返回 ASK 重定向,指引客户端到目标节点。
      3. 所有 key 迁移完成后,更新集群槽位映射表(所有节点同步新映射)。

四、缓存问题与解决方案

  1. 什么是缓存穿透?如何解决?

    • 缓存穿透:查询不存在的数据(如 id=-1 的用户),缓存和数据库都无结果,导致请求每次都穿透到数据库,压垮 DB。
    • 解决方案:
      • 空值缓存 :缓存不存在的 key(如 id=-1 → null),设置短期过期时间(避免缓存大量空值)。
      • 布隆过滤器:提前将所有存在的 key 存入布隆过滤器,请求先过过滤器,不存在则直接返回(误判率低,适合海量数据)。
  2. 什么是缓存击穿?如何解决?

    • 缓存击穿:热点 key 突然过期,此时大量请求同时穿透到数据库,导致 DB 压力骤增。
    • 解决方案:
      • 热点 key 永不过期:在缓存层不设置过期时间,由业务层定期更新(适合非实时数据)。
      • 互斥锁 :第一个请求获取锁后查询 DB 并更新缓存,其他请求等待锁释放后从缓存获取(用 Redis 的 setnx 实现锁)。
      • 预热与过期时间错开:提前加载热点数据,设置随机过期时间(避免大量 key 同时过期)。
  3. 什么是缓存雪崩?如何解决?

    • 缓存雪崩:大量 key 同时过期,或缓存集群宕机,导致所有请求穿透到数据库,DB 被压垮。
    • 解决方案:
      • 过期时间错开 :给 key 过期时间加随机值(如 expire + 1~5 分钟),避免同时过期。
      • 缓存集群高可用:部署主从 + 哨兵或集群,避免单点故障(如一台缓存机宕机,其他节点仍可用)。
      • 限流降级:用 Sentinel/Hystrix 对 DB 限流,超出阈值则返回降级数据(如 "系统繁忙")。
      • 多级缓存:本地缓存(如 Caffeine)+ 分布式缓存(Redis),减少分布式缓存压力。
  4. 缓存与数据库一致性如何保证?

    • 核心原则:最终一致性 (强一致性难实现,需权衡性能)。常见方案:
      • 更新策略 :先更数据库,再删缓存(而非更新缓存)------ 避免 "脏写"(如 A 更新 DB 后更新缓存,B 同时更新 DB 并覆盖缓存,导致 A 的更新丢失)。

        java

        运行

        复制代码
        // 伪代码
        updateDB();    // 先更新数据库
        deleteCache(); // 再删除缓存(下次查询会从 DB 加载最新数据到缓存)
      • 延迟双删:解决 "删除缓存失败" 的问题 ------ 更新 DB 后删缓存,隔一段时间(如 500ms)再删一次(应对第一次删除失败)。

      • 读写分离场景:主从复制延迟可能导致 "查从库时缓存未更新",可在删除缓存后,短暂禁止从库读(或设置缓存过期时间小于主从延迟)。

五、过期策略与内存管理

  1. Redis 的过期键删除策略是什么?

    • Redis 用三种策略结合处理过期键:
    • 惰性删除:访问 key 时才检查是否过期,过期则删除(节省 CPU,可能浪费内存)。
    • 定期删除:每隔一段时间(默认 100ms),随机抽查部分过期 key 并删除(平衡 CPU 和内存)。
    • 内存淘汰机制 :当内存达到 maxmemory 阈值,触发淘汰策略(如删除部分 key 释放内存)。
  2. Redis 的内存淘汰机制有哪些?

    • 当内存满时,根据配置的 maxmemory-policy 淘汰 key:
      • volatile-lru:从设置了过期时间的 key 中,淘汰最近最少使用的。
      • allkeys-lru:从所有 key 中,淘汰最近最少使用的(最常用)。
      • volatile-lfu:从设置过期时间的 key 中,淘汰最不经常使用的。
      • allkeys-lfu:从所有 key 中,淘汰最不经常使用的。
      • volatile-random:从设置过期时间的 key 中,随机淘汰。
      • allkeys-random:从所有 key 中,随机淘汰。
      • volatile-ttl:从设置过期时间的 key 中,淘汰剩余 TTL 最小的。
      • noeviction:不淘汰,返回错误(默认,不推荐)。

六、高级特性与实践

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

    • 分布式锁用于解决分布式系统中资源竞争问题(如秒杀库存),Redis 实现核心是 set 命令:

      bash

      复制代码
      # 加锁:key=lock:xxx,value=唯一标识(如UUID),NX(不存在才设置),PX(过期时间,防死锁)
      SET lock:product:1001 uuid-xxx NX PX 30000
    • 解锁:需用 Lua 脚本保证原子性(先判断 value 是否为自己的标识,再删除): lua

      复制代码
      if redis.call('get', KEYS[1]) == ARGV[1] then
          return redis.call('del', KEYS[1])
      else
          return 0
      end
    • 问题:单点 Redis 可能宕机,可用 RedLock 算法(多个 Redis 实例加锁,超过半数成功才认为锁有效)。

  2. Redis 的大 key 有什么危害?如何处理?

    • 危害:
      • 占用大量内存,导致内存分布不均;
      • 序列化 / 反序列化耗时,影响性能;
      • 删除大 key 可能阻塞主线程(如 del 一个百万元素的 Hash)。
    • 处理:
      • 拆分大 key :如将大 Hash 拆分为多个小 Hash(user:1000 → user:1000:infouser:1000:orders)。
      • 渐进式删除 :用 hscan/sscan 分批删除大 key 的元素(如每次删 100 个),避免阻塞。
      • 监控预警 :通过 redis-cli --bigkeys 定期检测大 key,设置阈值(如 >10MB 告警)。
  3. Redis 事务的特性是什么?有什么局限性?

    • 特性:Redis 事务通过 multi(开始)、exec(执行)、discard(取消)实现,支持:
      • 批量执行命令(exec 时一次性执行);
      • 原子性(要么全执行,要么全不执行,中间命令失败不回滚)。
    • 局限性:
      • 没有隔离级别(事务执行期间,其他客户端的命令可能插入);
      • 命令错误分为 "语法错"(exec 前报错,事务取消)和 "运行错"(如对 String 用 hsetexec 会继续执行,不回滚);
      • 不支持回滚(设计上为了性能,避免回滚日志开销)。
相关推荐
点灯小铭5 小时前
基于单片机的多模式自动洗衣机设计与实现
数据库·单片机·嵌入式硬件·毕业设计·课程设计
潜心编码5 小时前
基于python的仓库管理系统
数据库
herinspace5 小时前
如何设置电脑分辨率和显示缩放
服务器·数据库·智能手机·电脑
biubiubiu07065 小时前
Ubuntu中定时任务测试
数据库·postgresql
程序新视界6 小时前
在MySQL中,一条SQL语句的执行全流程是怎样的?
数据库·后端·mysql
todoitbo7 小时前
我用 TRAE 做了一个不一样的 MySQL MCP
数据库·mysql·adb·ai工具·mcp·trae·mysql-mcp
CodeJourney.7 小时前
Python开发可视化音乐播放器教程(附代码)
数据库·人工智能·python
呆呆小金人7 小时前
SQL入门:正则表达式-高效文本匹配全攻略
大数据·数据库·数据仓库·sql·数据库开发·etl·etl工程师
白鲸开源8 小时前
(二)从分层架构到数据湖仓架构:数据仓库分层下的技术架构与举例
大数据·数据库·数据分析