Files
zhuangang/topics/backend/redis/interview.md
T

66 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: backend.redis.interview
title: Redis 面试题
type: interview
domain: backend
stack: [redis]
tags: [Redis, 缓存, 分布式锁, 面试]
level: intermediate
related: [backend.redis.data-structures, backend.redis.cache-patterns, backend.redis.lock-ratelimit]
week: [3, 14, 16]
---
# Redis 面试题
## 基础
### 【基础】Redis 为什么快?
**参考答案**:数据在内存里;核心命令执行是单线程的,没有锁竞争和线程切换;使用 IO 多路复用(epoll)同时处理大量连接;数据结构针对场景做了优化(如压缩列表、跳表)。Redis 6 起网络读写可以多线程,命令执行仍是单线程。
### 【基础】Redis 有哪些数据类型?各举一个使用场景。
**参考答案**:String(缓存、计数器、验证码)、Hash(对象、购物车)、List(最新消息列表)、Set(标签、去重、共同关注)、ZSet(排行榜、延迟队列)、Stream(轻量消息队列);另有 Bitmap(签到)、HyperLogLog(UV 统计)、Geo(附近的人)。
### 【基础】过期的 key 是怎么删除的?内存满了怎么办?
**参考答案**:惰性删除(访问时发现过期就删)+ 定期删除(周期性抽样检查过期 key)。内存达到 `maxmemory` 时按淘汰策略处理:`noeviction`(拒绝写入)、`allkeys-lru`、`allkeys-lfu`、`volatile-lru`(只淘汰设置了过期的 key)等,缓存场景常用 `allkeys-lru` / `allkeys-lfu`。
### 【基础】RDB 和 AOF 的区别?
**参考答案**:RDB 定时生成内存快照,文件小、恢复快,但可能丢失最后一次快照之后的数据;AOF 追加记录写命令,可配置每秒刷盘,丢数据更少,但文件更大,需要重写压缩。生产常两者都开,Redis 4 起支持混合持久化。
## 进阶
### 【进阶】缓存穿透、击穿、雪崩分别是什么?怎么解决?
**参考答案**:穿透是查询不存在的数据,每次都打到数据库——缓存空值、参数校验、布隆过滤器。击穿是热点 key 过期瞬间大量请求同时回源——互斥锁 / singleflight 只让一个请求回源,或逻辑过期后台刷新。雪崩是大量 key 同时过期或 Redis 不可用——过期时间加随机值、Redis 高可用、服务限流降级。
### 【进阶】怎么保证缓存和数据库的一致性?
**参考答案**:常用旁路缓存:先更新数据库,再删除缓存。删除而不是更新,是为了避免并发写导致缓存写入旧值。仍有极小概率不一致,可以用延迟双删、订阅 binlog 异步删除、短 TTL 兜底。缓存只能做到最终一致,要求强一致的数据(余额、库存最终值)以数据库为准。
**追问**:为什么不先删缓存再更新数据库?→ 删除后、更新前的读请求会把旧值写回缓存,不一致窗口更大。
### 【进阶】Redis 分布式锁怎么实现?有什么问题?
**参考答案**:`SET key 随机令牌 NX PX 过期时间` 加锁;用 Lua 脚本比较令牌后删除来解锁,防止误删别人的锁。问题:业务执行时间超过过期时间会导致两个持有者同时存在(续期 / 看门狗);主从异步复制,主节点宕机时锁可能丢失(Redlock 有争议);所以关键业务还要用数据库唯一约束、乐观锁等做最终保证。
### 【进阶】Redis 的主从、哨兵、集群分别解决什么问题?
**参考答案**:主从复制解决读扩展和数据冗余;哨兵(Sentinel)监控主节点,故障时自动选新主,解决高可用;Cluster 把数据按 16384 个槽分到多个主节点,解决容量和写扩展,每个主节点也可以有从节点。Cluster 下多 key 操作(包括 Lua)要求 key 在同一个槽,可以用哈希标签 `{}`。
## 场景题
### 【场景题】秒杀场景怎么防止超卖和重复购买?
**参考答案**:库存提前加载到 Redis;Lua 脚本原子完成“是否已购买 → 库存是否充足 → 扣减 → 记录用户”;成功后发 MQ 异步创建订单,数据库唯一约束兜底;失败或超时未支付回补库存;前端、网关层层限流。详见[库存预扣场景](scenarios/stock-reservation.md)。
### 【场景题】线上 Redis 突然变慢,怎么排查?
**参考答案**:`SLOWLOG GET` 看慢命令(`KEYS *`、大 key 的 `HGETALL` / `DEL`、复杂 Lua);`INFO` 看内存、连接数、命中率、是否在做 RDB / AOF 重写(fork 导致的卡顿);`--bigkeys` / `MEMORY USAGE` 找大 key;检查是否有热点 key 集中在一个节点。处理:改用 `SCAN`、`UNLINK`,拆分大 key,热点 key 加本地缓存。
### 【场景题】一个接口要按用户限流,每分钟最多 60 次,多实例部署,怎么做?
**参考答案**:用 Redis 共享计数。简单做法是固定窗口 `INCR` + 过期时间;要求精确时用 ZSET 滑动窗口(Lua 脚本里删除过期记录、计数、写入);允许突发用令牌桶。返回 429 并带上重试时间。要考虑 Redis 故障时的策略(放行还是拒绝)。详见[分布式锁和限流](03-lock-ratelimit.md)。