背景与动机
Redis 是 Java 后端最常用的内存键值数据库,常用于缓存、计数器、会话、排行榜、消息发布订阅。Spring Boot 项目里常通过 Jedis 或 Lettuce(Spring Data Redis 默认)访问。理解其数据结构、持久化和高可用方案,才能正确做缓存设计与故障处理。
核心原理拆解
1. Redis 是什么
- 基于内存的 NoSQL KV 存储,key 为字节数组(字符串)
- 单线程处理命令(6.0+ 网络 IO 可多线程,命令执行仍主线程),避免锁竞争
- 官方标称可达极高 QPS(数据在内存、IO 多路复用)
Java 侧类似 JDBC 标准,常用 Jedis(含连接池 JedisPool)或 Lettuce。
2. 五种基本数据结构
| 类型 | 说明 | 典型场景 |
|---|---|---|
| String | 字符串、数字、二进制 | JSON 缓存、计数器、点赞数 |
| List | 双向链表 | 队列、栈、时间线 |
| Hash | 字段-值映射 | 对象属性、分组 |
| Set | 无序不重复 | 标签、共同好友 |
| ZSet | 有序集合 | 排行榜 |
应用场景示例:
- String:缓存对象、视频点赞计数
- List:模拟队列;朋友圈内容 + 点赞列表(
user:x:post:x+post:x:good) - Hash:存用户对象字段、按组聚合
3. 为什么单线程还很快
- 数据主要在内存
- IO 多路复用(epoll 等),单线程处理大量连接
- 无锁竞争,避免上下文切换开销
4. 发布订阅
Redis 支持 Pub/Sub:发布者发频道,订阅者收消息。实现简单,但无消息堆积与回溯,复杂场景用 Kafka、RocketMQ。哨兵节点也通过频道交换监控信息(约每 2 秒)。
5. 持久化:RDB 与 AOF
| 方式 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| RDB(默认) | bgsave fork 子进程写快照,定时全量 | 文件紧凑,恢复快 | 非实时,fork 有开销 |
| AOF | 每条写命令追加到日志 | 更实时 | 文件大,需 rewrite,恢复慢 |
生产常 RDB + AOF 组合,兼顾恢复速度与数据安全。
6. 主从复制与哨兵(Sentinel)
主从:主写从读,数据异步复制。
主挂了:手动切换麻烦 → 哨兵模式(2.8+)自动监控与故障转移。
哨兵流程概要:
- 每 10s
INFO拉拓扑;每 2s 向频道发主观判断;每 1sPING心跳 - 主观下线:某哨兵认为节点不可达
- 客观下线:主节点主观下线时,半数以上哨兵同意
- 选举领导者:Raft 思想,谁先提出客观下线谁易当选
- 故障转移:选举新主节点
7. Redis 集群(3.0+)
采用虚拟槽(16384 slots),节点间 ping/pong 传播槽与故障信息;主观下线(pfail)→ 客观下线(fail)→ 选举领导者 → 故障转移。与哨兵类似,面向分片 + 高可用,适合大数据量。
8. 缓存与 MySQL 一致性
常见策略(按业务选,没有银弹):
- Cache Aside:读先缓存、miss 查 DB 再写缓存;写先更新 DB 再删缓存
- 过期策略:TTL + 主动失效
- 双写一致性:延迟双删、Canal 订阅 binlog 等(高一致场景)
9. Key 过期与内存淘汰
过期删除:
- 惰性:访问 key 时才检查是否过期(省 CPU,可能占内存)
- 定期:周期性抽样删除过期 key(折中)
因此设置了 5 分钟 TTL,到期后内存仍可能偏高——部分 key 尚未被访问或定期清理未扫到。
内存淘汰策略(内存不足时):
noeviction:报错(默认)allkeys-lru/allkeys-randomvolatile-lru/volatile-random/volatile-ttl(仅带过期的 key)
10. 缓存问题与应对
| 问题 | 含义 | 思路 |
|---|---|---|
| 穿透 | 查不存在的数据,缓存和 DB 都不命中,压垮 DB | 缓存空值(注意 TTL)、布隆过滤器 |
| 击穿 | 热点 key 过期瞬间大量请求打 DB | 互斥锁、逻辑过期 |
| 雪崩 | 大量 key 同时过期或缓存宕机,请求涌向 DB | 过期时间加随机、集群高可用、限流降级 |
| 热点 key | 单个 key QPS 极高 | 本地缓存、多副本、拆分 |
无底洞:集群节点过多,批量操作跨节点网络次数增加,性能反而下降——控制粒度、使用 hash tag 把相关 key 放同一槽。
11. 缓存粒度
- 粗粒度:一个 key 存整页数据,命中率高,更新成本高
- 细粒度:按字段或实体拆分,更新灵活,key 数量多
按读多写少、更新频率选择。
常见陷阱与错误示例
1. 把 Redis 当可靠消息队列
Pub/Sub 不持久化,消费者离线会丢消息;严肃场景用专业 MQ。
2. 缓存空值不设过期
恶意穿透时大量空 key 占满内存,必须短 TTL。
3. 主从异步复制读从库
主从延迟时可能读到旧数据,强一致读主或等复制。
面试高频问题
完整 15 道速查见 Redis 面试题 15 道。
一句话总结
Redis = 内存 + 单线程 + 多路复用;用对数据结构和高可用(主从/哨兵/集群);做缓存时同时设计过期、淘汰、穿透/雪崩/热点和与 DB 的一致性策略。