目录
[1. 面试考点地图](#1. 面试考点地图)
[2. Redis 基础认知](#2. Redis 基础认知)
[2.1. 什么是 Redis?有什么特点?](#2.1. 什么是 Redis?有什么特点?)
[2.2. Redis 为什么把数据放到内存中](#2.2. Redis 为什么把数据放到内存中)
[2.3. Redis 常见应用场景](#2.3. Redis 常见应用场景)
[3. 数据类型与底层编码](#3. 数据类型与底层编码)
[3.1. Redis 支持哪些数据类型](#3.1. Redis 支持哪些数据类型)
[3.2. 底层数据结构和编码方式](#3.2. 底层数据结构和编码方式)
[3.3. ZSet 为什么用跳表,不用红黑树](#3.3. ZSet 为什么用跳表,不用红黑树)
[4. 线程模型与网络 IO](#4. 线程模型与网络 IO)
[4.1. Redis 为什么是单线程模型](#4.1. Redis 为什么是单线程模型)
[4.2. Redis 的 IO 多路复用是怎么回事](#4.2. Redis 的 IO 多路复用是怎么回事)
[5. 持久化机制](#5. 持久化机制)
[5.1. RDB](#5.1. RDB)
[5.2. AOF](#5.2. AOF)
[5.3. AOF 重写机制](#5.3. AOF 重写机制)
[5.4. RDB 和 AOF 如何选择](#5.4. RDB 和 AOF 如何选择)
[6. 过期、淘汰与内存管理](#6. 过期、淘汰与内存管理)
[6.1. 如何设置 key 过期时间](#6.1. 如何设置 key 过期时间)
[6.2. Redis 的 key 过期删除策略](#6.2. Redis 的 key 过期删除策略)
[6.3. 大量 key 同时过期会怎样](#6.3. 大量 key 同时过期会怎样)
[6.4. Redis 淘汰策略](#6.4. Redis 淘汰策略)
[7. 高可用:主从、哨兵、集群](#7. 高可用:主从、哨兵、集群)
[7.1. 主从复制](#7.1. 主从复制)
[7.2. 哨兵 Sentinel](#7.2. 哨兵 Sentinel)
[7.3. Redis Cluster 集群](#7.3. Redis Cluster 集群)
[7.4. 一致性哈希](#7.4. 一致性哈希)
[8. 事务、Pipeline 与消息队列](#8. 事务、Pipeline 与消息队列)
[8.1. Redis 事务](#8.1. Redis 事务)
[8.2. Pipeline](#8.2. Pipeline)
[8.3. Redis 作为消息队列](#8.3. Redis 作为消息队列)
[9. 缓存与分布式实战](#9. 缓存与分布式实战)
[9.1. 分布式锁](#9.1. 分布式锁)
[9.2. 缓存穿透、雪崩、击穿](#9.2. 缓存穿透、雪崩、击穿)
[9.3. 热 key 问题](#9.3. 热 key 问题)
[9.4. Redis 和 MySQL 双写一致性](#9.4. Redis 和 MySQL 双写一致性)
[9.5. bigkey 问题](#9.5. bigkey 问题)
[10. 运维、协议与高级命令](#10. 运维、协议与高级命令)
[10.1. 测试连通性](#10.1. 测试连通性)
[10.2. 常用管理命令](#10.2. 常用管理命令)
[10.3. 为什么生产禁用 KEYS *](#10.3. 为什么生产禁用 KEYS *)
[10.4. Redis 网络协议](#10.4. Redis 网络协议)
[10.5. 查找附近的人](#10.5. 查找附近的人)
[11. 面试速查表](#11. 面试速查表)
1. 面试考点地图
Redis 面试通常围绕以下主线展开:
- 基础认知:是什么、为什么快、为什么放内存、应用场景
- 数据类型:String、List、Hash、Set、ZSet,以及 Bitmap、HyperLogLog、GEO、Stream
- 底层编码:raw、int、embstr、ziplist、listpack、quicklist、hashtable、intset、skiplist
- 线程模型:单线程、6.0 多线程、IO 多路复用、epoll
- 持久化:RDB、AOF、混合持久化、AOF 重写
- 内存管理:过期删除、淘汰策略、内存用尽
- 高可用:主从复制、哨兵、集群、哈希槽、一致性哈希
- 功能特性:事务、Pipeline、消息队列、分布式锁
- 缓存实战:穿透、雪崩、击穿、热 key、bigkey、双写一致性
- 运维与协议:常用命令、RESP、SCAN、GEO
面试时不要只背答案,最好按"结论 → 原理 → 场景 → 权衡 → 版本差异"回答
2. Redis 基础认知
2.1. 什么是 Redis?有什么特点?
Redis 是一个高性能的key-value内存数据库。它不同于 MySQL 这类关系型数据库:
- 数据主要存储在内存中,也支持持久化到硬盘
- 不使用"表"组织数据,而是使用键值对
- 没有复杂查询、外键、事务隔离级别等关系型数据库能力,换来的是简单、灵活和高性能
Redis 的典型特点:
- 内存存储,性能极高
- 支持多种数据结构
- 支持持久化:RDB、AOF
- 核心命令执行是单线程模型
- 支持主从复制、哨兵、集群
- 支持事务、Lua 脚本、Pipeline
- 支持多语言客户端
- 支持发布订阅、Stream 消息队列
- 高版本支持多线程网络 IO
Redis 本质是一个基于内存的 KV 数据库,常用于缓存、计数器、排行榜、分布式锁、消息队列等场景。它的核心优势是快,原因是内存访问、单线程无锁、IO 多路复用、高效数据结构。缺点主要是内存成本高、容量有限、持久化和主从复制存在一致性权衡
2.2. Redis 为什么把数据放到内存中
核心原因是 效率
内存随机访问大约 100ns,硬盘随机访问大约 10,000,000ns,差距 3 到 4 个数量级。因此 Redis 比 MySQL 等磁盘数据库有显著性能优势
但内存存储也有劣势:
- 内存容量比硬盘小,成本更高
- 掉电易失,所以需要 RDB/AOF 持久化
- 数据量受内存限制,需要淘汰策略和集群分片
2.3. Redis 常见应用场景
- 缓存:热点数据缓存,降低数据库压力
- 计数器:点击量、访问量、收藏数
- 排行榜:基于 ZSet 实现
- 分布式会话:多模块共享 Session
- 分布式锁:SET NX + 过期时间 + Lua 释放
- 消息队列:List、Pub/Sub、Stream
- 附近的人:GEO
- 签到、UV 统计:Bitmap、HyperLogLog
- 限流:计数器、滑动窗口、令牌桶
3. 数据类型与底层编码
3.1. Redis 支持哪些数据类型
| 类型 | 说明 | 典型场景 |
|---|---|---|
| String | 字符串、整数、二进制 | 缓存、计数器、分布式锁 |
| List | 双向列表 | 消息队列、时间线 |
| Hash | 哈希表 | 对象属性、购物车 |
| Set | 无序集合 | 去重、共同好友、标签 |
| ZSet | 有序集合 | 排行榜、延迟队列 |
扩展类型:
- Bitmap:二进制位,适合签到、活跃用户统计
- Bitfield:把字符串当位图并进行位操作
- HyperLogLog:基数统计,用极小内存估算 UV,有误差
- Geospatial:地理经纬度,支持附近查询
- Stream:消息队列,支持消费组、ACK、持久化
前五种是通用类型,后几种是特定场景类型
3.2. 底层数据结构和编码方式
Redis 会根据数据量、元素长度、版本自动选择编码。常用查看命令:
object encoding key
常见对应关系:
| 数据类型 | 内部编码 |
|---|---|
| String | int、embstr、raw |
| Hash | ziplist/listpack、hashtable |
| List | ziplist、linkedlist、quicklist |
| Set | intset、hashtable、listpack |
| ZSet | ziplist/listpack、skiplist |
关键点:
- int:字符串是整数且范围合适时,直接存整数
- embstr:短字符串优化,通常小于等于 39 字节
- raw:长字符串
- ziplist:压缩列表,本质是字节数组,省内存,但增删可能连锁更新
- listpack:Redis 7 开始引入,用来替代 ziplist,解决连锁更新问题
- quicklist:链表,每个节点是 ziplist/listpack,兼顾内存和性能
- hashtable:真正哈希表,O(1) 查找
- intset:整数集合,适合小整数集合
- skiplist:跳表,支持 O(logN) 查找、插入、删除和范围查询
3.3. ZSet 为什么用跳表,不用红黑树
跳表和红黑树的插入、查询、删除复杂度都是 O(logN),但 Redis 选择跳表:
- 跳表实现更简单,代码可维护性高
- 跳表不需要红黑树那样的旋转和重新平衡
- 跳表天然支持范围查询,ZRANGE、ZREVRANGE 更方便
- 跳表可以通过概率平衡,实现难度低
- Redis 是单线程,简单稳定比极致复杂更重要
跳表不会像普通链表那样退化,因为层级通过随机函数生成,期望复杂度 O(logN)
4. 线程模型与网络 IO
4.1. Redis 为什么是单线程模型
Redis 的主要瓶颈通常不是 CPU,而是 内存 和 网络 IO。使用多线程不会带来太大收益,反而引入线程安全、锁竞争、上下文切换问题
单线程的好处:
- 命令串行执行,天然避免并发安全问题
- 不需要复杂锁机制
- 实现简单,行为可预测
- 配合 IO 多路复用,可以高效处理大量连接
Redis并不是完全单线程。它通过后台线程或子进程来处理一些耗时任务:
- 持久化:通过fork()创建子进程进行RDB快照和AOF重写
- 异步删除:使用UNLINK等命令,由后台线程负责回收内存
- AOF刷盘:在默认的everysec策略下,fsync操作由后台线程执行;若设为always,则由主线程同步执行
- 网络I/O(Redis 6.0+):使用多个I/O线程并行处理网络请求和协议解析,但命令执行仍是单线程
4.2. Redis 的 IO 多路复用是怎么回事
"IO 多路复用"就是用一个线程管理多个 socket,按需激活处理。
传统"一连接一线程"模式下,大量连接可能大部分时间不活跃,线程都在挂起,浪费资源。Redis 使用 Linux 的 epoll:
- 内核维护红黑树管理所有 socket
- 每个节点关联事件回调
- 网卡收到数据后,内核判断属于哪个 socket,触发回调,唤醒用户线程
- Redis 线程处理就绪的请求
Linux IO 多路复用三种实现:
- select:最早,有文件描述符数量限制
- poll:链表实现,没有数量限制,但仍需轮询
- epoll:最先进,事件驱动,适合高并发
Redis 高并发不是因为多线程,而是因为单线程 + epoll + 内存操作 + 高效数据结构
Redis 的高效数据结构,指的是它为不同数据类型定制的底层编码:String 用 SDS,支持 O(1) 长度获取和二进制安全;Hash 用 dict + 渐进式 rehash,小数据量时用 listpack/ziplist 压缩存储;List 用 quicklist,兼顾内存和操作效率;Set 用 intset 或 dict;ZSet 用 listpack + skiplist,兼顾排序和范围查询;还有 rax、HyperLogLog、GEO 等专用结构。它们通过紧凑内存布局、按数据量自动切换编码、减少指针和内存碎片、提供 O(1)/O(log N) 操作,让 Redis 在单线程下也能以极低 CPU 和内存开销处理海量请求
5. 持久化机制
Redis 持久化主要有两种:RDB 和 AOF。Redis 4.0 后支持混合持久化
5.1. RDB
RDB 是内存快照,把某一时刻数据以二进制写入磁盘
触发方式:
- 手动:SAVE、BGSAVE
- 自动:
- 配置 save m n:m 秒内发生 n 次修改触发
- 从节点全量复制触发
- 执行 SHUTDOWN 关闭 Redis 时触发
区别:
- SAVE:阻塞主线程,期间无法处理写请求
- BGSAVE:fork 子进程生成 RDB,父进程继续处理请求
- 子进程写 RDB 期间,新写入数据不一定进入当前 RDB,需要下一轮持久化
优点:文件紧凑、恢复快、适合备份
缺点:可能丢数据,fork 有开销
5.2. AOF
AOF 记录写命令,重启时重放命令恢复数据
AOF 同步策略:
| 策略 | 说明 | 丢失风险 |
|---|---|---|
| always | 每条命令 fsync 后返回 | 几乎不丢,但性能差 |
| everysec | 先 write,每秒 fsync | 最多丢 1 秒 |
| no | 只 write,由 OS 控制 fsync | 丢失可能较多 |
5.3. AOF 重写机制
AOF 文件会越来越大,需要重写压缩,过程简述:
- 触发 BGREWRITEAOF 或自动重写
- fork 子进程,根据当前内存数据生成新 AOF
- 主进程继续处理命令,同时写入 AOF 重写缓冲区
- 子进程完成后,主进程把重写期间新命令追加到新 AOF
- 用新 AOF 替换旧 AOF
AOF 重写会阻塞吗?
fork 瞬间可能阻塞,之后子进程重写,主进程继续服务
但 fork 成本与内存页表有关,内存越大越慢
5.4. RDB 和 AOF 如何选择
- 只做缓存:可以只开 RDB 或不开
- 数据不能丢太多:AOF everysec + RDB
- 恢复速度优先:RDB
- 数据安全优先:AOF
- 生产常见:混合持久化,RDB 快照 + AOF 增量
6. 过期、淘汰与内存管理
6.1. 如何设置 key 过期时间
SET key value EX 60
SET key value PX 60000
EXPIRE key 60
PEXPIRE key 60000
EX 是秒,PX 是毫秒。也可以单独用 EXPIRE
6.2. Redis 的 key 过期删除策略
Redis 同时使用:
- 惰性过期:访问 key 时才判断是否过期,过期则删除。省 CPU,但可能浪费内存
- 定期过期:每隔 100ms 随机抽取一定数量 key 检查删除。CPU 和内存折中
expires 字典保存所有设置了过期时间的 key 及其过期时间
6.3. 大量 key 同时过期会怎样
如果大量 key 在同一时间过期,Redis 可能短暂卡顿,原因:
- 定期采样删除时,如果过期 key 比例超过 25%,会持续删除,直到最大耗时 25ms
- 对高并发场景,25ms 阻塞也可能造成抖动
解决方案:
- 过期时间加随机值,打散过期点
- 业务允许时,不必精确到毫秒
- 监控过期 key 数量,避免集中过期
6.4. Redis 淘汰策略
当内存不足,继续写入会触发淘汰策略:
| 策略 | 说明 |
|---|---|
| volatile-lru | 从设置了过期时间的 key 中按 LRU 淘汰 |
| allkeys-lru | 从所有 key 中按 LRU 淘汰 |
| volatile-lfu | 从过期 key 中按 LFU 淘汰,4.0+ |
| allkeys-lfu | 从所有 key 中按 LFU 淘汰,4.0+ |
| volatile-random | 从过期 key 中随机淘汰 |
| allkeys-random | 从所有 key 中随机淘汰 |
| volatile-ttl | 从过期 key 中优先淘汰更早过期的 |
| noeviction | 默认,不淘汰,写入报错 |
LRU:最近最久未使用
LFU:最近使用频率最低
- 默认 noeviction 能暴露问题,但很多缓存场景会用 allkeys-lru
- 如果数据重要,不要用 allkeys-*
- 应该有监控报警,内存接近上限就扩容
- Redis 内存耗尽时,会触发淘汰;noeviction 下写入报错
7. 高可用:主从、哨兵、集群
7.1. 主从复制
作用:
-
提高可用性,主节点挂了可以从节点读
-
分担读压力,主写从读
配置: slaveof master_ip master_port
或配置文件 replicaof
流程:
- 从节点启动后清空自身数据,全量复制主节点数据
- 后续主节点写命令同步到从节点
- 主节点可读写,从节点默认只读
优点:读扩展、数据备份
缺点:主节点故障需要手动或哨兵切换;异步复制可能丢数据
7.2. 哨兵 Sentinel
哨兵解决主从故障自动切换
功能:
- 监控主从节点
- 主观下线、客观下线
- 选举 Leader 哨兵
- 从从节点中选新主
- 通知客户端新主地址
- 继续监控
主节点宕机后:
- 单个哨兵认为主下线,是主观下线
- 多个哨兵确认,是客观下线
- 哨兵选举 Leader
- Leader 选择合适从节点提升为主
- 其他从节点指向新主
- 通知客户端
7.3. Redis Cluster 集群
集群用于分片存储,解决单机内存和性能上限
特点:
- 数据分片,每个主节点负责一部分槽
- 共有 16384 个哈希槽
- 槽计算:CRC16(key) mod 16384
- 至少 3 主 3 从,主从保证高可用
- 支持 MOVED、ASK 重定向
- 不支持 SELECT 多数据库,只能使用 db0
- 异步复制,特定条件下可能丢写
最大节点数:作者建议不要超过 1000 个,不是硬限制
7.4. 一致性哈希
一致性哈希用于分布式缓存分片
原理:
- 把哈希值空间组织成环
- 节点和数据都映射到环上
- 数据顺时针找到第一个节点
- 增加虚拟节点解决数据倾斜
优点:扩缩容时只影响相邻数据,迁移量小
缺点:实现复杂,需要虚拟节点,仍有倾斜可能
Redis Cluster 没有用一致性哈希,而是用哈希槽,因为槽更易管理、迁移和重新分配
8. 事务、Pipeline 与消息队列
8.1. Redis 事务
相关命令:
- MULTI:开启事务
- EXEC:执行事务
- DISCARD:取消事务
- WATCH:乐观锁,监视 key
特点:
- 命令入队,EXEC 时按顺序执行
- 不支持回滚。运行时错误,其他命令仍可能继续执行
- 不是 MySQL 那种强原子、隔离级别、回滚事务
- 更像"命令打包串行执行"
- WATCH 可实现乐观锁,若 key 被修改则 EXEC 失败
和 MySQL 区别:
| 维度 | Redis | MySQL |
|---|---|---|
| 原子性 | 弱,不支持回滚 | 强,支持回滚 |
| 隔离性 | 单线程串行 | 多隔离级别 |
| 持久性 | 取决于持久化 | 取决于日志和配置 |
| 复杂度 | 简单 | 复杂 |
8.2. Pipeline
Pipeline 把多个命令合并到一个请求发送,减少 RTT
普通模式:
发送命令 -> 排队 -> 执行 -> 返回
发送命令 -> 排队 -> 执行 -> 返回
Pipeline:
发送多个命令 -> 排队 -> 执行 -> 一次性返回
注意:
- Pipeline 命令 不是原子的
- 只是节省网络往返
- 命令太多可能阻塞服务器
- 可用 cat cmd.txt | redis-cli --pipe
8.3. Redis 作为消息队列
三种方式:
- List:LPUSH/RPUSH 生产,BLPOP/BRPOP 阻塞消费
- Pub/Sub:发布订阅,但不持久化,消费者离线会丢消息
- Stream:Redis 5 引入,支持消费组、ACK、持久化,功能更完整
Redis Pub/Sub 是发布订阅模式,用 PUBLISH 发消息,SUBSCRIBE 订阅频道,PSUBSCRIBE 模式订阅。它的特点是实时推送,但不持久化、不存储、无 ACK,消费者离线就丢消息,投递语义是至多一次。所以它只适合实时通知、广播这类场景,不能当可靠消息队列用。可靠消息一般用 Redis Stream 或者 Kafka/RabbitMQ。Sentinel 内部就用 Pub/Sub 来互相通知
9. 缓存与分布式实战
9.1. 分布式锁
基本实现:
SET lock_key unique_value NX PX 30000
释放锁要用 Lua 保证原子:
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
要点:
- value 必须唯一,防止误删别人的锁
- 必须设置过期时间,防止死锁
- 释放锁必须原子
- 业务未完成可续期,看门狗机制
- 主从切换可能丢锁,强一致场景考虑 Redlock 或 ZooKeeper
- Redlock 有争议,面试可提"多数派加锁,但时钟漂移和网络延迟仍有挑战"
Redlock 是 Redis 作者提出的分布式锁算法:部署多个独立 Redis 节点,客户端在多数派节点上加锁成功才认为获取锁,以此提高可靠性。但它依赖各节点本地时钟判断锁过期,并假设网络延迟有界;时钟漂移、GC 停顿、网络抖动都可能导致锁提前失效,或客户端在锁已过期后仍误以为自己持有锁,从而破坏互斥。因此 Redlock 无法保证绝对安全,业界对其争议较大
ZooKeeper 和 etcd 都是分布式协调服务,核心是在分布式环境中提供强一致、高可用的元数据存储与协调能力。ZooKeeper 基于 ZAB 协议,通过 ZNode 树、临时顺序节点和 Watch 实现分布式锁、选主、服务发现和配置管理;etcd 基于 Raft 协议,提供键值存储、租约 Lease、Watch 和事务,是 Kubernetes 的核心数据存储。两者都依赖多数派共识,不依赖物理时钟来判断锁安全,因此在强一致要求的分布式锁、选主等场景中,比 Redis Redlock 更可靠。区别上,ZooKeeper 较老、Java 生态常用;etcd 更轻量、云原生生态更主流。
9.2. 缓存穿透、雪崩、击穿
| 问题 | 含义 | 解决方案 |
|---|---|---|
| 缓存穿透 | 查不存在的数据,请求打到 DB | 缓存空值、布隆过滤器、参数校验 |
| 缓存雪崩 | 大量 key 同时过期或 Redis 宕机 | 随机过期、多级缓存、限流熔断、集群 |
| 缓存击穿 | 热点 key 过期,大量请求打到 DB | 互斥锁、逻辑过期、永不过期、后台更新 |
9.3. 热 key 问题
某些 key 访问频率极高,可能打挂 Redis。集群下,热 key 仍在同一分片,其他分片帮不上忙
解决:
- 扩大集群,增加热 key 所属分片的从节点
- 应用层识别热 key,做二次哈希,拆成多个 key
- 热 key 单独部署 Redis 集群
- 使用本地缓存,减少 Redis 压力
- 限流、降级、熔断
9.4. Redis 和 MySQL 双写一致性
双写一致性指修改数据库时,也要更新或删除缓存,否则缓存可能是脏数据
方案一:延时双删
- 先删除缓存
- 更新数据库
- 再次删除缓存
目的:第一次删除失败,第二次兜底
因为直接改缓存可能并发覆盖,不如删除缓存,后续读时从 DB 加载
方案二:删除缓存重试
- 删除失败把 key 放入 MQ,反复重试
- 可用阿里 canal 订阅 MySQL binlog,异步删除缓存
binlog 订阅是指通过 Canal、Debezium、Maxwell 等工具伪装成 MySQL 从库,向主库发送 dump 协议请求,实时接收并解析 binlog,从而捕获数据库的增删改操作。在缓存一致性场景中,应用更新数据库后,订阅程序监听到对应变更事件,再异步删除或更新缓存,避免业务代码耦合双写,实现最终一致;此外也常用于数据同步、审计、搜索索引更新等。
方案三:先更新 DB,再删缓存
这是常见 Cache Aside 模式
Cache Aside 是旁路缓存模式,应用先读缓存,未命中查数据库并回填;写时先更新数据库,再删除缓存。它简单常用,能保证最终一致,但并发下可能短暂脏读,可通过延迟双删、binlog 订阅、过期时间兜底
9.5. bigkey 问题
bigkey 指 value 占用空间过大,比如超长字符串、超大 Hash/Set/List
危害:
- 读写慢,阻塞 Redis
- 网络拥塞
- 集群数据倾斜
- 删除时可能阻塞
解决:
- 拆分大 key 为多个小 key
- 用 redis-cli --bigkeys 查找
- 删除用 UNLINK,后台异步删除
- 集合遍历用 HSCAN、SSCAN、ZSCAN
10. 运维、协议与高级命令
10.1. 测试连通性
PING
返回 PONG 即连通
10.2. 常用管理命令
DBSIZE # 当前数据库 key 数量
INFO # 服务器状态和统计
MONITOR # 实时监听请求
SHUTDOWN # 保存并关闭
CONFIG GET parameter # 获取配置
CONFIG SET parameter value
DEBUG OBJECT key # 查看 key 调试信息
DEBUG SEGFAULT # 制造宕机,慎用
FLUSHDB # 删除当前库,慎用
FLUSHALL # 删除所有库,慎用
10.3. 为什么生产禁用 KEYS *
KEYS * 会遍历所有 key,数据量大时耗时极长,甚至阻塞 Redis,导致生产故障
替代:SCAN
SCAN cursor [MATCH pattern] [COUNT count]
特点:
- 渐进式遍历,每次 O(1)
- 返回下一次游标
- 游标为 0 表示结束
- 可能重复或遗漏
- 还有 HSCAN、SSCAN、ZSCAN
SCAN 是渐进式遍历,不保证快照一致性。遍历期间哈希表 rehash 或元素被修改,会导致同一元素重复返回,或被删除的元素不再返回。对于遍历期间一直存在的元素,SCAN 保证至少返回一次,但可能重复
10.4. Redis 网络协议
Redis 使用 RESP,Redis Serialization Protocol,应用层纯文本协议
客户端和服务器通过 RESP 通信。注意不是 RSP
10.5. 查找附近的人
使用 GEO:
GEOADD key 经度 纬度 member ...
GEORADIUS key 经度 纬度 距离
底层基于 ZSet 和 Geohash。适合附近的人、打车、门店查询
11. 面试速查表
| 问题 | 一句话答案 |
|---|---|
| Redis 是什么 | 高性能内存 KV 数据库 |
| 为什么快 | 内存、单线程、epoll、高效数据结构 |
| 五种类型 | String、List、Hash、Set、ZSet |
| ZSet 为什么跳表 | 实现简单、范围查询方便、无需重平衡 |
| 单线程原因 | 瓶颈在内存/IO,不在 CPU,避免锁 |
| 6.0 多线程 | 网络 IO 和协议解析多线程,命令执行单线程 |
| IO 多路复用 | epoll 管理大量 socket,事件驱动 |
| 持久化 | RDB、AOF、混合持久化 |
| RDB 触发 | save/bgsave、save m n、全量复制、shutdown |
| AOF 同步 | always、everysec、no |
| 过期删除 | 惰性 + 定期 |
| 大量 key 同时过期 | 随机过期时间打散 |
| 淘汰策略 | LRU、LFU、random、ttl、noeviction |
| 主从作用 | 读扩展、备份、故障恢复 |
| 哨兵作用 | 监控、选主、自动故障转移 |
| 集群分片 | 16384 哈希槽 |
| 集群选库 | 不支持,只能 db0 |
| 集群丢数据 | 异步复制,可能丢写 |
| 事务命令 | MULTI、EXEC、DISCARD、WATCH |
| 事务区别 | 不支持回滚,弱原子 |
| Pipeline | 减少 RTT,非原子 |
| 消息队列 | List、Pub/Sub、Stream |
| 分布式锁 | SET NX PX + Lua 释放 + 续期 |
| 缓存穿透 | 缓存空值、布隆过滤器 |
| 缓存雪崩 | 随机过期、多级缓存、熔断 |
| 缓存击穿 | 互斥锁、逻辑过期 |
| 热 key | 本地缓存、拆分、单独集群 |
| 双写一致 | 延时双删、删除重试、binlog |
| bigkey | 拆分、UNLINK、SCAN 遍历 |
| 遍历 key | SCAN,不用 KEYS * |
| 附近的人 | GEOADD、GEORADIUS |