摘要:Redis作为高性能缓存与内存数据库,是后端开发的核心技术栈之一。本文整理20大高频Redis面试题,结合真实场景与底层源码逻辑,助你彻底掌握Redis核心机制。涵盖单线程模型、集群方案、分布式锁、持久化等核心知识点。
问题:Redis为何采用单线程模型?如何实现高并发?
原理剖析:
基于内存操作:数据存储于内存,读写速度远超磁盘
I/O多路复用:通过epoll/kqueue实现非阻塞I/O,单线程处理多连接
单线程优势:避免上下文切换与锁竞争,保证原子性操作
优化数据结构:全局哈希表+跳表等高效结构,时间复杂度O(1)/O(logN)
场景化答案:
在电商秒杀场景中,Redis单线程处理10万QPS的库存扣减请求。虽然单线程,但通过内存操作和I/O多路复用,避免了多线程锁竞争的开销,同时保证原子性(如DECR操作)。
问题:大量缓存同时失效,请求直接打到数据库
解决方案:
随机过期时间:expire key 3600 + random(600)
二级缓存:本地缓存+Redis双层结构
熔断降级:Hystrix限流保护DB
问题:查询不存在的数据(如id=-1)
解决方案:
布隆过滤器:预加载合法key,拦截非法请求
空值缓存:SET null 60
避免重复查询
问题:热点key过期瞬间高并发请求
解决方案:
互斥锁:SETNX lock_key 1
控制重建缓存的并发
逻辑过期:缓存永不过期,后台异步更新
场景案例:
社交媒体热点新闻缓存突然失效,使用Redis分布式锁(Redisson)控制只有一个请求重建缓存,其他请求等待后直接读取新值。
特性 | RDB | AOF |
---|---|---|
持久化方式 | 内存快照 | 追加写操作日志 |
数据安全性 | 可能丢失最后一次快照后数据 | 可配置fsync策略(每秒/始终) |
恢复速度 | 快 | 慢(需重放命令) |
文件大小 | 小(二进制压缩) | 大(文本格式) |
场景选择:
数据备份:RDB定时全量备份
金融交易:AOF appendfsync always保证强一致性
混合模式:Redis 4.0后支持RDB+AOF混合持久化
问题:Redis事务是否满足ACID?
原理解析:
原子性:命令队列EXEC执行,但不支持回滚(部分失败继续执行)
隔离性:单线程执行,天然串行化隔离级别
持久性:依赖持久化配置(AOF+fsync)
一致性:通过单线程和错误检测保证
场景化示例:
MULTI
INCR stock:1001
EXPIRE stock:1001 60
EXEC
使用Lua脚本保证库存扣减与过期时间设置的原子性,避免部分成功。
同步流程:全量同步(RDB)+增量同步(repl_backlog)
读写分离:Master写,Slave读(注意数据延迟问题)
数据分片:16384个slot,CRC16(key) % 16384
节点通信:Gossip协议维护集群状态
故障转移:投票机制选举新Master
场景问题:
某节点宕机后,客户端如何重定向?
答案:返回MOVED错误,客户端更新本地slot缓存。
# redis.conf配置
maxmemory-policy volatile-lru
LRU:最近最少使用(采样近似算法)
LFU:4.0+引入,基于访问频率
TTL:淘汰即将过期的键
场景选择:
会员系统:volatile-lru保留常访问用户数据
实时日志:allkeys-lru优先保留新数据
原理:将多个命令打包发送,减少RTT(Round Trip Time)
with r.pipeline() as pipe:
for user_id in user_ids:
pipe.get(f"user:{user_id}")
results = pipe.execute()
性能对比:
单命令:100次操作 = 100次网络延迟 + 100次执行时间
Pipeline:100次操作 = 1次网络延迟 + 100次执行时间
核心变化:
主线程:单线程处理命令执行
IO线程:多线程处理网络IO(默认关闭,需配置)
io-threads 4 # 启用4个IO线程
适用场景:
大value读取(如1MB以上的数据)
高并发连接数(万级以上)
问题:如何用Redis实现可靠的分布式锁?
原理剖析:
基础命令:SET lock_key unique_value NX EX 30
(原子性设置键值+过期时间)
释放锁:Lua脚本验证值匹配后删除,避免误删其他客户端锁
RedLock算法:
向5个独立节点申请锁
多数节点(≥3)获取成功且总耗时小于锁有效期
场景化答案:
在物流系统调度车辆时,使用Redisson实现的RedLock防止多个调度中心同时分配同一车辆。若某个Redis节点宕机,仍能通过多数派机制保证锁有效性。
-- 释放锁脚本示例
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
数据类型 | 底层结构 | 典型场景 |
---|---|---|
String | SDS动态字符串 | 计数器(INCR)、缓存JSON字符串 |
Hash | 哈希表/ziplist | 存储用户信息(字段独立更新) |
List | 双向链表/ziplist | 消息队列(LPUSH+BRPOP)、最新消息排行 |
Set | 哈希表/intset | 共同关注(SINTER)、抽奖去重(SADD) |
ZSet | 跳表+哈希表 | 排行榜(ZRANGE)、延迟队列(按分数排序) |
场景案例:
在线教育平台使用ZSet存储课程销量排行榜,score为销量值,每售出一单执行
ZINCRBY courses:sales 1 course_id
,实时获取Top10课程。
问题:新Slave节点加入时如何同步数据?
原理解析:
全量同步:
Slave发送PSYNC ? -1
请求
Master生成RDB快照并缓冲期间写命令
增量同步:
Master维护环形缓冲区(repl_backlog)
Slave断线重连后发送PSYNC replid offset
关键配置:
repl-backlog-size 64mb # 缓冲区大小
问题:网络分区导致数据不一致如何处理?
解决方案:
min-slaves配置:
min-slaves-to-write 1 # 至少1个Slave在线才允许写
min-slaves-max-lag 10 # Slave延迟不超过10秒
故障转移:通过Gossip协议触发选举
定义:String>10KB,Hash/List>5000元素
解决方案:分拆存储、数据压缩、使用替代结构
解决方案:本地缓存、多副本分散、限流降级
定位方法:
slowlog-log-slower-than 10000 # 记录超过10ms的命令
slowlog-max-len 128
常见慢操作:KEYS *
、大Value操作、复杂Lua脚本
监测指标:
redis-cli info memory | grep ratio
优化方案:重启节点、自动碎片整理(4.0+)、控制键过期
维度 | Redis | Memcached |
---|---|---|
数据结构 | 5种基础+扩展类型 | 仅String类型 |
持久化 | RDB/AOF | 无持久化 |
适用场景 | 复杂操作(排行榜、队列) | 简单键值缓存 |
惰性删除:访问时检查
定期删除:每100ms随机抽查
消息不持久化
无ACK机制(推荐改用Streams)
特性 | Pipeline | 事务(MULTI/EXEC) |
---|---|---|
原子性 | 无 | 有 |
返回结果 | 一次性获取 | 依次返回 |
架构:
主线程处理命令执行
IO线程池负责网络IO
io-threads 4 # 启用4个IO线程
io-threads-do-reads yes
掌握Redis需深入理解其设计哲学与底层机制。建议结合源码(如ae_eventloop.c、dict.c)学习,并在项目中实践缓存设计模式。关注最新特性如Redis 7.0的Function和Multi-part AOF。
延伸学习:
Redis源码分析
Redlock分布式锁实现
缓存与数据库双写一致性方案
关注+私信回复“Redis”获取《Redis核心原理深度笔记》PDF资料
整合后的内容涵盖Redis核心机制、生产级解决方案和面试高频考点,适合开发者系统学习和面试准备。建议结合线上环境实操验证理论。