Redis常见面试题
1、简述下什么是 Redis?
答: Redis(Remote Dictionary Server)是一个开源的、基于内存的高性能键值对(key-value)数据库,支持多种数据结构,可用作数据库、缓存、消息队列和流处理引擎。其特点包括:
- 高性能:读写速度可达 10万+ QPS
- 丰富的数据类型:支持 String、List、Set、Hash、ZSet 等多种数据结构
- 持久化支持:支持 RDB 和 AOF 两种持久化方式
- 高可用:支持主从复制、哨兵模式、Cluster 集群
- 原子性操作:所有操作都是原子性的,支持事务
2、Redis 五大数据类型是什么?分别适用于什么场景?
答:
| 数据类型 | 底层实现 | 适用场景 |
|---|---|---|
| String | SDS(简单动态字符串) | 缓存、计数器、分布式锁、Session 共享 |
| List | 双向链表 / 压缩列表(ziplist) | 消息队列、时间线、最新消息列表 |
| Set | 整数集合(intset)/ 哈希表 | 标签系统、共同好友、抽奖、去重 |
| Hash | 压缩列表 / 哈希表 | 对象存储(如用户信息)、购物车 |
| ZSet | 压缩列表 / 跳表(Skip List)+ 哈希表 | 排行榜、延迟队列、范围查询 |
补充:Redis 5.0+ 还增加了 Stream 类型,用于实现消息队列(类似 Kafka)。
3、Redis 持久化有几种方式?他们的区别又是什么?
答: 两种:RDB 和 AOF,以及 RDB + AOF 混合模式(Redis 4.0+)
| 特性 | RDB | AOF | 混合模式 |
|---|---|---|---|
| 原理 | 定时快照,保存内存数据的全量副本 | 记录所有写操作命令日志 | RDB 全量 + AOF 增量 |
| 文件体积 | 紧凑,经压缩 | 较大,可重写压缩 | 适中 |
| 恢复速度 | 快(直接加载二进制) | 慢(需重放命令) | 快(先加载 RDB,再重放 AOF) |
| 数据安全 | 可能丢失最后一次快照后的数据 | 最多丢失 1 秒数据(默认配置) | 兼顾两者优点 |
| 性能影响 | fork 子进程时短暂阻塞 | 持续写入,性能损耗较小 | 综合两者 |
| 优先级 | 低 | 高(同时开启时优先加载 AOF) | - |
RDB 触发条件:
- 手动:
SAVE(阻塞)/BGSAVE(后台) - 自动:配置
save 900 1(900秒内1次修改触发)
AOF 重写机制(BGREWRITEAOF):
- 当 AOF 文件过大时,fork 子进程创建新的 AOF 文件,只保留最终状态的命令
- 重写期间的新写入会同时写入旧 AOF 和 AOF 重写缓冲区
4、Redis 为什么单线程还这么快?
答: Redis 6.0 之前是单线程模型,但性能极高的原因:
- 纯内存操作:所有数据在内存中,读写速度在纳秒级别
- 避免上下文切换:单线程无需 CPU 切换线程上下文,无锁竞争
- 高效的数据结构:如 SDS、跳表、压缩列表等,针对场景优化
- IO 多路复用:使用 epoll/kqueue/select 处理高并发连接,单线程可同时监听多个 socket
- 非阻塞 IO:网络 IO 使用非阻塞模式,配合事件循环(Reactor 模式)
Redis 6.0+ 多线程优化:网络 IO 采用多线程(默认关闭),命令执行仍是单线程,进一步提升吞吐量。
5、Redis 的事务机制是什么?有什么局限性?
答: Redis 事务通过 MULTI、EXEC、DISCARD、WATCH 实现:
MULTI # 开启事务,命令进入队列
SET k1 v1 # 入队
SET k2 v2 # 入队
EXEC # 执行队列中的所有命令WATCH 乐观锁机制:
WATCH key # 监视键,如果 EXEC 前被修改,事务放弃
MULTI
...
EXEC # 返回 nil 表示事务未执行事务特性:
- ✅ 原子性:事务中的命令要么都执行,要么都不执行(但无回滚)
- ❌ 不支持回滚:某条命令执行失败,后续命令仍继续执行
- ❌ 非隔离性:事务执行过程中,其他客户端命令可能插入执行
与 ACID 对比: Redis 事务不满足传统 ACID,更接近"批量执行脚本"。
6、Redis 的缓存穿透、缓存击穿和缓存雪崩是什么?如何解决?
答:
| 问题 | 现象 | 根本原因 | 解决方案 |
|---|---|---|---|
| 缓存穿透 | 查询不存在的数据,请求直达 DB | 恶意攻击或业务误传无效 ID | ① 布隆过滤器(Bloom Filter)预判;② 缓存空值(设置短过期时间);③ 接口校验 |
| 缓存击穿 | 热点 key 过期瞬间,大量请求打爆 DB | 高并发 + 热点 key 过期 | ① 互斥锁(SETNX),仅一个线程回源;② 逻辑过期(不设置 TTL,通过逻辑时间判断);③ 热点 key 永不过期 |
| 缓存雪崩 | 大量 key 同时过期,DB 压力剧增 | 批量设置相同过期时间或 Redis 宕机 | ① 随机过期时间(基础值+随机值);② 多级缓存(本地+Redis);③ 熔断降级(Hystrix/Sentinel);④ 高可用架构(Cluster/哨兵) |
布隆过滤器原理: 通过多个哈希函数映射到 bitmap,存在误判(可能误判存在,不会误判不存在),适合过滤明显无效请求。
7、Redis 的持久化文件如何备份与恢复?
答:
备份方式:
RDB 文件备份(
dump.rdb)- 定时 copy 到异地(如 OSS、NFS)
- 使用
redis-cli --rdb backup.rdb在线导出 - 配置
dir和dbfilename指定路径
AOF 文件备份(
appendonly.aof)- 直接复制 AOF 文件(支持热备,Redis 会创建当前状态的副本)
- 定期执行
BGREWRITEAOF压缩后备份
自动化工具
- Redis-shake:阿里云开源的 Redis 数据迁移/同步工具
- rdbtools:分析 RDB 文件内容
恢复流程:
- 停止 Redis 实例
- 将备份的 RDB/AOF 文件放入配置目录
- 启动 Redis,自动加载(AOF 优先级高于 RDB)
- 验证数据完整性
8、Redis 主从复制原理是什么?有哪些复制方式?
答:
复制流程(全量 + 增量):
- 建立连接:从节点发送
SLAVEOF命令 - 全量同步(RDB):主节点执行
BGSAVE,生成 RDB 发送给从节点 - 命令传播:主节点将写命令持续发送给从节点(复制积压缓冲区)
- 增量同步:网络中断后,通过
repl_backlog_buffer和offset进行部分重同步(PSYNC)
复制方式演进:
- Redis 2.8 前:SYNC,只能全量同步
- Redis 2.8+:PSYNC,支持部分重同步
- Redis 4.0+:PSYNC2,解决主从切换后仍需全量同步的问题
主从架构问题:
- 主节点故障需手动切换 → 引入 哨兵模式(Sentinel)
- 写性能受限于单主节点 → 引入 Cluster 集群
9、Redis 哨兵(Sentinel)机制是什么?
答: 哨兵是 Redis 的高可用解决方案,实现自动故障转移。
核心功能:
- 监控:周期性检查主从节点健康(PING)
- 通知:通过发布订阅通知客户端故障转移
- 自动故障转移:
- 主观下线(SDOWN):单个 Sentinel 认为节点不可达
- 客观下线(ODOWN):多数 Sentinel 同意,触发故障转移
- 选举新主节点(优先级 > 复制偏移量 > Run ID)
- 更新其他从节点指向新主节点
- 配置提供者:客户端连接 Sentinel 获取当前主节点地址
最小部署: 3 个 Sentinel 节点(防止脑裂,需奇数个)
10、Redis Cluster 集群原理是什么?数据如何分布?
答: Redis Cluster 是官方分布式方案,实现数据分片和高可用。
核心机制:
数据分片(Sharding):
- 16384 个哈希槽(Slot),每个 key 通过
CRC16(key) % 16384映射到槽 - 每个主节点负责一部分槽(如 3 主节点,各负责 5461/5461/5462 个槽)
- 16384 个哈希槽(Slot),每个 key 通过
节点通信:
- Gossip 协议:节点间互相交换状态信息(槽分布、节点标识等)
- 每秒随机 ping 几个节点,超过一半节点认为某节点故障则标记为 FAIL
请求路由:
- MOVED 重定向:key 不在当前节点,返回
MOVED slot node_ip:port - ASK 重定向:槽正在迁移时临时跳转
- Smart Client:客户端缓存槽映射,减少重定向
- MOVED 重定向:key 不在当前节点,返回
故障转移:
- 主节点故障时,从节点发起选举(基于 Raft 算法)
- 获得多数主节点投票后晋升为新主节点
架构限制:
- 不支持多 key 操作(除非 keys 在同一槽,可用 Hash Tag:
{user:1000}.name和{user:1000}.age) - 批量操作(mget/mset)需使用 pipeline 或 Hash Tag
11、Redis 内存淘汰策略有哪些?
答: 当内存达到 maxmemory 限制时,触发淘汰策略:
| 策略 | 说明 | 适用场景 |
|---|---|---|
| volatile-lru | 从已设置过期时间的 key 中,淘汰最近最少使用(LRU) | 大部分 key 有过期时间 |
| allkeys-lru | 从所有 key 中淘汰 LRU | 希望保留热数据 |
| volatile-lfu | 从过期 key 中淘汰最少频率使用(LFU,4.0+) | 考虑访问频率 |
| allkeys-lfu | 从所有 key 中淘汰 LFU | 长期热数据保留 |
| volatile-random | 随机淘汰过期 key | 无明显冷热区分 |
| allkeys-random | 随机淘汰所有 key | 一般不推荐 |
| volatile-ttl | 淘汰即将过期的 key(TTL 小的) | 希望尽快释放内存 |
| noeviction | 不淘汰,直接返回错误(默认) | 不允许丢数据 |
LRU/LFU 实现: Redis 采用近似算法,每个对象记录 24bit 的 LRU 时钟或 16bit 的 LFU 计数器,牺牲精确度换取性能。
12、Redis 常见的性能优化手段有哪些?
答:
| 层面 | 优化手段 |
|---|---|
| 命令优化 | 避免 O(N) 命令(KEYS → SCAN,SMEMBERS → SSCAN);禁用危险命令(FLUSHALL/CONFIG,用 rename-command) |
| Pipeline | 批量操作使用 pipeline 减少 RTT(往返时间),mget/mset 替代多次 get/set |
| 连接优化 | 使用连接池,避免频繁创建连接;考虑 Unix Domain Socket(本机) |
| 内存优化 | 使用 Hash 存储对象(ziplist 编码省内存);设置合理的 maxmemory 和淘汰策略 |
| 架构优化 | 读写分离(从节点读);分片(Cluster);本地缓存(Caffeine/Guava)+ Redis 二级缓存 |
| 持久化优化 | 主节点建议不开启 AOF 或配置 appendfsync everysec;从节点开启 AOF |
| 系统优化 | 关闭透明大页(THP);设置 vm.overcommit_memory=1;网络带宽充足 |
13、Redis 与 Memcached 的区别?
答:
| 特性 | Redis | Memcached |
|---|---|---|
| 数据类型 | 丰富(5+ 种) | 仅 String |
| 持久化 | 支持 RDB/AOF | 不支持 |
| 集群 | 官方 Cluster | 需客户端实现(如一致性哈希) |
| 线程模型 | 单线程(6.0+ 多线程 IO) | 多线程 |
| 内存管理 | 支持内存淘汰策略 | LRU 淘汰, slab 分配 |
| 事务 | 支持(MULTI/EXEC) | 不支持 |
| 发布订阅 | 支持 | 不支持 |
| 地理空间 | 支持(GEO 命令) | 不支持 |
| 适用场景 | 复杂数据结构、持久化、消息队列 | 纯缓存、简单 KV、多核 CPU 优化 |
14、Redis 的大 Key 和热 Key 问题如何排查和解决?
答:
大 Key 问题:
- 危害:阻塞主线程(删除/序列化耗时)、内存不均、网络拥塞
- 排查:
redis-cli --bigkeys扫描(采样,非精确)MEMORY USAGE key查看具体 key 内存占用rdbtools分析 RDB 文件
- 解决:
- 拆分:Hash 分桶(如
user:1000:profile拆为多个 field) - 压缩:序列化使用 msgpack/kryo 替代 JSON
- 异步删除:UNLINK(4.0+,后台线程释放内存)替代 DEL
- 拆分:Hash 分桶(如
热 Key 问题:
- 危害:单节点 QPS 过高、CPU 打满、主从复制延迟
- 排查:
redis-cli --hotkeys(需配置maxmemory-policy为 LFU)- 监控
INFO stats中的keyspace_hits - 业务层埋点统计
- 解决:
- 本地缓存(一级缓存)
- 读写分离:多个从节点分担读压力
- 拆分:将热 key 分散(如
counter:1拆为counter:1_1、counter:1_2)
15、Redis 脑裂问题是什么?如何解决?
答:
现象: 主从网络分区时,原主节点仍在接收写请求,导致数据不一致,网络恢复后旧主节点被降级为从节点,数据被清空,造成数据丢失。
解决: 配置两个参数(Redis 2.8+):
min-slaves-to-write 1 # 至少 1 个从节点连接时,主节点才接受写
min-slaves-max-lag 10 # 从节点延迟超过 10 秒,主节点停止写原理: 网络分区时,原主节点因无足够从节点连接而拒绝写,减少数据不一致窗口。
16、Redis 运维中常见的缓存问题有哪些?
答: 主要有三类:缓存穿透、缓存击穿、缓存雪崩,以及 缓存一致性、缓存预热、缓存降级 等问题。
17、什么是缓存穿透?如何解决?
答: 查询不存在的数据,请求直接打到数据库(如 id=-1 或恶意构造不存在的 key)。
解决方案:
- 布隆过滤器:前置过滤无效请求
- 缓存空值:将 null 也缓存,设置短过期时间(如 30 秒)
- 接口校验:参数合法性检查,拦截明显非法请求
18、什么是缓存击穿?如何解决?
答: 热点 key 过期瞬间,大量并发请求同时打到数据库。
解决方案:
- 互斥锁:SETNX 抢锁,仅一个线程回源,其他等待
- 逻辑过期:不设置 TTL,通过逻辑时间戳判断是否过期,异步重建
- 热点 key 永不过期:后台定时异步刷新
19、什么是缓存雪崩?如何解决?
答: 大量 key 同时过期 或 Redis 宕机,导致请求集中压垮数据库。
解决方案:
- 随机过期时间:基础时间 + 随机偏移(如 300s + 0-60s)
- 多级缓存:本地缓存(Caffeine)+ Redis,降低单点压力
- 高可用架构:Redis Cluster / 哨兵,避免单点故障
- 熔断降级:Hystrix/Sentinel 限流,保护后端 DB
- 提前预热:大促前主动加载热点数据,避免冷启动
20、缓存与数据库数据不一致怎么办?
答: 常见场景:更新数据时,缓存和 DB 数据不一致。
解决方案:
- Cache Aside 模式:先更新 DB,再删缓存(非更新缓存)
- 延迟双删:删缓存 → 更新 DB → 延迟(如 500ms)→ 再删缓存
- 消息队列:通过 MQ 异步同步,保证最终一致性
- Canal 订阅 Binlog:监听 MySQL binlog,异步更新/删除缓存
21、什么是缓存预热?如何实现?
答: 系统上线或重启后,提前将热点数据加载到缓存,避免冷启动时大量请求直接访问 DB。
实现方式:
- 定时任务扫描热点 key,主动加载
- 系统启动时读取配置文件中的预热数据列表
- 通过数据分析预测热点,提前灌入
22、什么是缓存降级?何时使用?
答: 缓存层故障或压力过大时,有损服务,保证核心功能可用。
降级策略:
- 直接返回默认值或空值(非关键数据)
- 限流:限制请求速率,保护后端
- 开关控制:动态关闭非核心功能(如推荐、评论)
- 走本地缓存或直连 DB(仅核心查询)
23、Redis 内存告警(OOM)如何处理?
排查:
redis-cli INFO memory # 查看内存使用
redis-cli --bigkeys # 找大 key
MEMORY DOCTOR # 内存诊断建议处理:
- 设置
maxmemory限制,配置淘汰策略(如allkeys-lru) - 删除大 key:使用
UNLINK(异步删除,避免阻塞) - 拆分大 Hash/Set,分散存储
- 开启内存碎片整理(
activedefrag yes,4.0+)
24、Redis 连接数爆满怎么处理?
排查:
redis-cli INFO clients # 查看连接数
redis-cli CLIENT LIST # 查看详细连接信息处理:
- 客户端使用连接池,避免短连接
- 设置
timeout 300,关闭空闲连接 - 检查是否有连接泄露(未 close)
- 限制最大客户端数
maxclients(默认 10000) - 检查是否有慢查询阻塞(
SLOWLOG GET 10)
25、Redis 主从同步延迟怎么排查?
排查命令:
redis-cli INFO replication # 查看 master_link_status、master_last_io_seconds_ago
redis-cli INFO stats # 查看 sync_partial_ok、sync_full 次数优化:
- 避免大 key,减少全量同步(RDB)压力
- 提升主从间网络带宽,降低延迟
- 适当增大
repl-backlog-size(复制积压缓冲区),减少 PSYNC 失败 - 业务层允许短暂延迟,或强制读主(
READWRITE)
26、Redis 持久化失败怎么处理?
答:
RDB 失败:
- 检查磁盘空间(
df -h)、目录权限 - 查看
logfile中BGSAVE错误日志 - 避免 fork 失败:检查
vm.overcommit_memory=1,减少大页内存
AOF 失败:
- 检查
appendonly.aof文件权限和磁盘空间 - AOF 重写失败:查看是否有大 key 导致内存不足
- 手动修复:
redis-check-aof --fix appendonly.aof
