六篇之后
前面六篇把 Redis 五种核心数据结构过了一遍。这篇不再引入新类型,而是做三件事:
- 把五种结构串起来,给一个选型速查
- 聊两个避不开的话题:持久化怎么配、缓存三大坑怎么防
- 指一下下一步学什么
五种数据结构选型速查
看到一个需求,脑子里自动匹配数据结构——学到这个程度,Redis 就算出师了。
一个简化的决策路径:
| 你的需求 | 用这个 | 为什么 |
|---|---|---|
| 缓存一个 API 响应,整进整出 | String | 最简单,设个 TTL 就完了 |
| 缓存一个对象,经常改个别字段 | Hash | 字段级读写,listpack 省内存 |
| 消息队列、最新动态、阻塞消费 | List | BRPOP 阻塞等,天然队列 |
| 去重、标签、共同好友、抽奖 | Set | 集合运算在服务端做完 |
| 排行榜、延迟队列、按范围取 | ZSet | score 自动排序,跳表 O(log N) |
两个高频纠结场景的具体判断:
对象存 String(JSON) 还是 Hash?
- 大部分时候只读写一两个字段 → Hash
- 永远是整进整出、嵌套很深 → String
- 字段数 3-5 个、都短 → 随便,差别不大
消息队列用 List 还是 Stream?
- 消息丢了无所谓、单消费者 → List 够用
- 消息不能丢、需要重试、多消费者并行 → Stream(Redis 5.0+)
- 超大规模、跨数据中心 → 用 Kafka/RabbitMQ,别用 Redis
持久化:数据丢了怎么办
Redis 是内存数据库,重启就没了。如果数据有价值,持久化是必须的。
三种方案:
RDB:定时快照
到时间了就 fork 一个子进程,把整个内存数据拍个快照存到 dump.rdb。
save 900 1 # 15 分钟内改了 1 个 key → 拍快照save 300 10 # 5 分钟内改了 10 个 key → 拍快照save 60 10000 # 1 分钟内改了 10000 个 key → 拍快照优点:文件小、恢复快。缺点:两次快照之间的数据丢了。适合缓存、排行榜这种丢了也能重建的数据。
AOF:写操作日志
每一条修改命令都记到文件里,重启时一条条重放。
appendonly yesappendfsync everysec # 每秒刷一次盘,丢最多 1 秒数据优点:数据安全性高。缺点:文件比 RDB 大、恢复比 RDB 慢。适合订单、支付这类不能丢的数据。
appendfsync 的三个选项:
always:每条命令都刷盘,最安全也最慢everysec:每秒刷一次,推荐值,丢最多 1 秒no:交给 OS,性能最好但可能丢几十秒
混合持久化:生产标配(Redis 4.0+)
aof-use-rdb-preamble yesAOF 重写时,先以 RDB 格式存全量快照,再把增量以 AOF 格式追加。结果是:安全性接近 AOF,恢复速度接近 RDB。
绝大多数生产环境直接开混合持久化 + appendfsync everysec,不用纠结。
一句话选型
| 场景 | 推荐 |
|---|---|
| 纯缓存,丢了无所谓 | 不用持久化 |
| 性能优先,可容忍分钟级丢失 | RDB |
| 数据不能丢 | 混合持久化 + everysec |
| 金融级不能丢 | AOF always + 主从 + 异地备份 |
缓存的三个噩梦
把 Redis 当缓存用,有三个经典故障模式。
缓存穿透:查不存在的数据
现象:有人疯狂查不存在的 user_id=-1,缓存里没有,数据库里也没有,每次都打到 DB。
防治:
- 空值缓存:查不到也缓存个空值,设短 TTL(30-60 秒)
- 布隆过滤器:先用极小内存判断”这个 key 绝对不存在”,挡掉大部分无效请求
- 入口参数校验:不合法的 ID 直接在网关层拒绝
缓存击穿:热点 key 过期
现象:秒杀商品的缓存刚好过期,几百个请求同时打进来,全去查数据库。
防治:
- 互斥锁:只让一个线程去查 DB 重建缓存,其余等着。用
SET NX PX实现,Redis 第二篇讲过 - 逻辑过期:值里存一个过期时间戳,缓存不设 TTL。读的时候发现”逻辑过期”了,返回旧值,异步刷新
缓存雪崩:大量 key 同时过期
现象:一批缓存设了相同的 TTL,过期瞬间所有请求全部砸向数据库。
防治:
- TTL 加随机:
TTL = 基准值 + random(0, 基准值*0.1),把过期时间打散。最简单也最有效 - 多级缓存:本地 Caffeine → Redis → DB 三级,每层兜底
- 熔断降级:DB 扛不住时返回兜底数据或降级页面
三个问题经常被混在一起说,记一个口诀:
穿透是”没有也查”,击穿是”热key过期”,雪崩是”大家一起过期”。
实际项目里,几个经验
别什么都往 Redis 里塞
刚学会 Redis 的时候容易把它当万能工具。几个自问帮你刹车:
- 这个数据丢了我能接受吗?不能 → 持久化开了吗?有备份吗?
- 这个 key 会变得很大吗?→ 会 → 拆成多个小 key
- 这个数据放 MySQL 里查一次也就 2ms,真的需要缓存吗?→ 不一定
Key 命名要有规范
三个月后回来看代码,a、x1、tmp 这些 key 你完全不知道是什么。养成好习惯:
业务:对象类型:ID:子属性
user:profile:1001order:detail:20250622001cache:article:list:page1lock:send_email:task过期的坑
EXPIRE 设了又设,要小心。如果业务逻辑里每次更新都重新 EXPIRE(比如 SET key val EX 600),过期时间会一直往后推。这是你想要的还是意外的?
另外——别依赖过期时间来保证业务正确性。过期是 Redis 的内存管理机制,不是业务定时器。Redis 的过期删除是惰性的(访问时才检查),不是精确到秒的闹钟。
下一步学什么
五种数据结构学完了,持久化搞清楚了,缓存三大坑也防住了——Redis 的”基础篇”到此结束。接下来可以往三个方向深入:
高可用方向:
- 主从复制 + 哨兵(Sentinel)—— 自动故障转移
- Redis Cluster —— 数据分片、水平扩展
进阶功能方向:
- Stream —— 可靠消息队列,ACK、消费者组
- Pub/Sub —— 发布订阅,即时通讯
- Lua 脚本 —— 把多个命令打包原子执行
- Pipeline —— 批量发送命令,减少网络往返
性能与原理方向:
- 内存淘汰策略(LRU/LFU)
- 渐进式 Rehash
- 多线程 I/O(Redis 6.0+)
- 大 Key 排查与治理
这是 Redis 学习系列的最后一篇。从”Redis 不就是个缓存吗”到”五种数据结构各司其职”,七篇文章走完了一个 Redis 初学者的完整路径。剩下的,就是去项目里用了。
回头去看第一篇时那个”我以为 Redis 就是个缓存”的自己——现在你应该能笑着理解他了。
部分信息可能已经过时





