java面试中经常会问到的Redis问题有哪些(基础版)
文章目录
- 一、Redis 基础与数据结构
- 二、持久化机制
- 三、高可用与集群
- 四、缓存相关问题
- 五、分布式锁
- 六、性能与优化
- 七、其他高频问题
- 总结
Redis 是 Java 后端面试中的高频考点,尤其在缓存、分布式锁、高并发场景中频繁出现。以下是常见问题及核心解析:
一、Redis 基础与数据结构
- 什么是 RedisRedis?它有哪些核心特性?
- Redis 是一款开源的 高性能键值对数据库(Key-Value Store),基于内存存储,支持持久化,常用于缓存、分布式锁、消息队列等场景。
- 核心特性:
- 基于内存,读写速度快(单机 QPS 可达 10 万+)。
- 支持多种数据结构(String、Hash、List、Set、Sorted Set 等)。
- 支持持久化(RDB、AOF)、主从复制、哨兵模式、集群(Cluster)。
- 支持事务、Lua 脚本、发布订阅等功能。
-
Redis 支持哪些数据结构?各自的应用场景是什么?
数据结构 特点/核心命令 典型场景 String 二进制安全,可存字符串、数字、二进制 缓存用户信息、计数器( incr
)、分布式 IDHash 键值对集合,适合存储对象 存储用户详情( hset user:1 name "xxx"
)List 有序列表,支持两端操作 消息队列( lpush
/rpop
)、最新列表Set 无序无重复集合,支持交集/并集运算 好友关系(共同好友 sinter
)、去重Sorted Set 有序集合(按 score 排序),支持范围查询 排行榜( zadd
/zrange
)、延时任务Bitmap 位操作,节省内存 签到统计( setbit
/bitcount
)HyperLogLog 基数统计(去重计数),占用内存极小 UV 统计( pfadd
/pfcount
) -
Redis 的 String 类型最大能存储多少数据?
- 最大 512MB。String 类型不仅能存文本,还能存二进制数据(如图片、序列化对象),但实际中通常只存小数据(如 JSON 字符串、令牌),避免大 Key 影响性能。
二、持久化机制
- Redis 的持久化方式有哪些?区别是什么?
RDB(Redis Database):
- 原理:在指定时间间隔内,将内存中的数据集快照写入磁盘(二进制文件
dump.rdb
)。- 触发方式:手动执行
save
(阻塞 Redis)或bgsave
(后台异步执行,fork 子进程处理);配置文件中设置自动触发(如save 60 1000
:60 秒内有 1000 次修改)。- 优点:文件小,恢复速度快;缺点:可能丢失最后一次快照后的所有数据(数据安全性低)。
AOF(Append Only File):
- 原理:记录每一条写命令(如
set
、hset
)到日志文件(appendonly.aof
),恢复时重新执行命令。- 同步策略:
appendfsync always
:每条命令都同步到磁盘(安全性最高,性能最差)。appendfsync everysec
:每秒同步一次(默认,平衡安全性和性能)。appendfsync no
:由操作系统决定何时同步(性能最好,安全性最低)。- 优点:数据安全性高(最多丢失 1 秒数据);缺点:文件大,恢复速度慢,需定期
bgrewriteaof
压缩(合并重复命令)。
- RDB 和 AOF 如何选择?
- 追求性能、可容忍数据丢失:选 RDB。
- 追求数据安全性(如金融场景):选 AOF。
- 实际生产中常 混合使用(Redis 4.0+ 支持 RDB-AOF 混合持久化,AOF 文件头部是 RDB 快照,后续是增量命令,兼顾速度和安全性)。
三、高可用与集群
- Redis 主从复制的原理?有什么作用?
- 原理:
- 从节点(Slave)启动时发送
SYNC
命令给主节点(Master)。- 主节点执行
bgsave
生成 RDB 文件,同时缓存期间的写命令。- 主节点发送 RDB 文件和缓存的命令给从节点,从节点加载 RDB 并执行命令,完成初始化同步。
- 后续主节点的写命令会实时同步给从节点(增量同步)。
- 作用:读写分离(主节点写,从节点读,分担读压力)、数据备份(从节点作为备份,避免主节点故障丢失数据)。
- 哨兵模式(Sentinel)的作用?工作原理是什么?
- 作用:监控主从节点状态,当主节点故障时自动将从节点升级为主节点(自动故障转移),保证 Redis 集群高可用。
- 工作原理:
- 哨兵节点定期向所有主从节点发送
PING
命令,检测节点是否存活。- 若主节点超过
down-after-milliseconds
未响应,哨兵标记其为“主观下线”。- 多个哨兵协商,若多数哨兵认为主节点下线,标记为“客观下线”。
- 哨兵选举出一个从节点升级为主节点,其他从节点改为新主节点的从节点。
- Redis Cluster 如何实现分片?为什么要设计 16384 个槽?
- 分片原理:Redis 集群将数据按 Key 的哈希值分配到 16384 个槽(slot) 中,每个节点负责一部分槽。
- 计算方式:
slot = CRC16(key) % 16384
。- 迁移:槽可以在节点间迁移,实现负载均衡。
- 16384 个槽的设计原因:
- 槽数量过多会增加节点间心跳包的大小(每个节点需在心跳中报告自己负责的槽);
- 过少则可能导致槽分配不均,且不利于扩展(如 1000 个槽最多支持 1000 个节点)。16384 是平衡后的结果(心跳包大小可控,支持足够多节点)。
四、缓存相关问题
- Redis 作为缓存时,如何解决缓存穿透、缓存击穿、缓存雪崩?
缓存穿透:查询不存在的数据(如 ID=-1 的用户),导致请求穿透缓存直达数据库,压垮 DB。
解决:
- 布隆过滤器:提前过滤不存在的 Key(如用 Redis 的 Bitmap 实现)。
- 缓存空值:对不存在的 Key 缓存空值(设置短期过期时间)。
缓存击穿:热点 Key 过期瞬间,大量请求直达 DB。
解决:
- 互斥锁:查询 DB 时加锁(如 Redis 的
setnx
),只允许一个线程更新缓存,其他线程等待。- 热点 Key 永不过期:或后台线程定时更新,避免过期。
缓存雪崩:大量缓存 Key 同时过期,或 Redis 集群宕机,导致请求全部压向 DB。
解决:
- 过期时间加随机值:避免 Key 集中过期(如
expire key 3600 + Math.random()*1000
)。- Redis 集群部署:主从 + 哨兵,避免单点故障。
- 服务熔断/限流:用 Sentinel 或网关限制 DB 的请求量。
- 缓存与数据库的一致性如何保证?
- 核心原则:先更新数据库,再更新缓存(或删除缓存),避免“更新缓存失败导致脏数据”。
- 常见方案:
- Cache Aside Pattern:读操作先查缓存,缓存未命中则查 DB 并回写缓存;写操作先更新 DB,再删除缓存(而非更新,避免并发问题)。
- 延迟双删:更新 DB 后删除缓存,隔一段时间(如 500ms)再删一次(解决删除缓存失败的极端情况)。
- 最终一致性:通过消息队列异步更新缓存(如更新 DB 后发送消息,消费消息时更新缓存)。
五、分布式锁
- 如何用 Redis 实现分布式锁?
- 核心命令:
set key value NX PX timeout
(NX
:仅当 Key 不存在时设置,PX
:设置过期时间,避免死锁)。- 步骤:
- 获取锁:
set lock:order 123 NX PX 30000
(123 是唯一标识,如 UUID)。- 执行业务逻辑。
- 释放锁:通过 Lua 脚本原子性删除(避免误删其他线程的锁):
lua if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end
- 问题与优化:
- 锁过期问题:用“看门狗”线程定时续期(如 Redisson 的
RLock
自动续期)。- 集群下的锁失效:Redis Cluster 中,主节点宕机可能导致锁丢失,需用 Redlock 算法(多节点加锁)。
- Redisson 分布式锁的优势?
- 自动续期(看门狗机制):避免锁过期。
- 支持公平锁、可重入锁。
- 集群环境下更可靠(基于 Redis 集群的 Redlock 实现)。
六、性能与优化
- Redis 为什么快?
- 基于内存操作,避免磁盘 IO 瓶颈。
- 单线程模型(核心操作单线程,避免线程切换开销)。
- 高效的数据结构(如跳表用于 Sorted Set,哈希表用于 String/Hash)。
- IO 多路复用(epoll/kqueue):单线程处理多个客户端连接。
- Redis 的单线程模型会影响性能吗?为什么?
- 通常不影响:Redis 的瓶颈是内存和网络,而非 CPU(单线程足以处理大部分场景)。
- 例外:执行复杂命令(如
keys *
、hgetall
遍历大 Hash)会阻塞单线程,导致其他请求超时。- 优化:避免复杂命令,用
scan
替代keys
,分批获取大集合数据。
- 如何优化 Redis 的性能?
- 合理设置过期时间,避免内存溢出(
maxmemory-policy
配置淘汰策略,如volatile-lru
)。- 避免大 Key(如存储 10 万元素的 List),拆分大 Key 为多个小 Key。
- 批量操作:用
pipeline
减少网络往返(如批量set
命令合并发送)。- 读写分离:主节点写,从节点读,分担压力。
七、其他高频问题
- Redis 的过期键删除策略?
- 惰性删除:访问 Key 时才检查是否过期,过期则删除(节省 CPU,可能浪费内存)。
- 定期删除:每隔一段时间扫描部分过期 Key 并删除(平衡 CPU 和内存)。
- 内存淘汰:当内存达到
maxmemory
时,按策略淘汰键(如volatile-lru
淘汰过期键中最近最少使用的)。
- Redis 事务的实现?与 ACID 的关系?
- 实现:通过
multi
(开始事务)、exec
(执行事务)、discard
(取消事务)命令,将多个命令放入队列,exec
时原子执行(要么全执行,要么全不执行)。- ACID 支持:
- 原子性(Atomicity):部分支持(若命令错误,事务会取消;若运行时错误,其他命令仍会执行)。
- 一致性(Consistency)、隔离性(Isolation):支持。
- 持久性(Durability):不支持(依赖持久化配置)。
- Redis 与 Memcached 的区别?
维度 Redis Memcached 数据结构 支持多种(String、Hash、List 等) 仅支持 String 持久化 支持 RDB、AOF 不支持 集群 原生支持 Cluster 需第三方工具(如 Codis) 内存管理 自己管理内存(支持过期键删除) 依赖操作系统 malloc/free,易产生内存碎片
总结
Redis 面试重点考察 数据结构应用、持久化机制、高可用方案、缓存问题(穿透/击穿/雪崩)、分布式锁实现 及 性能优化。回答时需结合实际场景(如缓存更新策略、分布式锁的安全隐患),体现对 Redis 底层原理和工程实践的理解。