Redis
Redis 是基于内存的高性能 Key-Value 数据库,常用于缓存、分布式锁、计数器、排行榜、限流、发布订阅、延迟队列等场景。
零基础可以先这样理解:
Redis 不是 MySQL 的替代品,而是用来解决“高频、低延迟、临时性或可重建数据访问”的工具。
为什么需要 Redis
数据库适合保存可靠的业务数据,但所有高频读取都直接打到数据库,会带来几个问题:
| 问题 | Redis 解决方式 | 如果不用会怎样 |
|---|---|---|
| 热点数据读取频繁 | 把热点数据放进内存缓存 | 数据库 QPS 高,接口响应慢 |
| 计数器高频自增 | Redis 原子自增命令 | 数据库行锁冲突严重 |
| 排行榜实时排序 | ZSet 按分数排序 | 数据库排序压力大 |
| 分布式互斥 | set nx ex 实现锁语义 | 多实例并发可能重复执行 |
| 临时状态保存 | TTL 自动过期 | 数据库堆积大量临时数据 |
Redis 快的原因不是一句“内存快”就能解释完:
- 数据主要在内存中,避免大量磁盘 IO。
- 单线程执行命令,减少锁竞争和线程切换。
- 使用 IO 多路复用处理大量连接。
- 数据结构为场景优化,例如 Hash、ZSet、Bitmap。
- 命令粒度小,很多操作是 O(1) 或 O(logN)。
Redis 在系统里的位置
mermaid
flowchart TD
A["用户请求"] --> B["应用服务"]
B --> C{"是否适合走 Redis"}
C -- "热点读取" --> D["Redis 缓存"]
C -- "强一致写入" --> E["MySQL"]
D -- "命中" --> F["快速返回"]
D -- "未命中" --> E
E --> G["写回缓存或返回结果"]要注意:Redis 通常保存“可重建数据”或“临时状态”。真正的核心业务事实,例如订单、支付流水、账户余额,仍然应该以数据库为准。
学习路线
mermaid
flowchart TD
A["基础命令<br/>key / ttl / string"] --> B["数据结构<br/>Hash / List / Set / ZSet"]
B --> C["缓存模式<br/>旁路缓存 / TTL / 更新策略"]
C --> D["持久化<br/>RDB / AOF"]
D --> E["高可用<br/>主从 / 哨兵 / Cluster"]
E --> F["治理问题<br/>穿透 / 击穿 / 雪崩"]
F --> G["工程实践<br/>分布式锁 / 限流 / 热 Key / 大 Key"]推荐阅读顺序:
- Redis 基础:命令和五大基础数据类型。
- 从零到生产级掌握:把数据结构、缓存模式、高并发问题、持久化、高可用、锁和排查连成课程线。
- 从零到精通验收清单:用可验证任务判断自己是不是真的会 Redis,而不是只背命令和面试题。
- 商业场景训练营:用资产详情缓存、穿透/击穿/雪崩、大 Key、热 Key、一致性、分布式锁、哨兵和 Cluster 把原理跑起来。
- 命令执行与数据结构原理:理解命令从网络事件到数据结构操作的全过程,以及 String、Hash、ZSet 等底层取舍。
- 过期删除与内存淘汰原理:理解 TTL、惰性删除、定期删除、LRU/LFU、内存满后的淘汰过程。
- Spring Boot 整合 Redis:Java 项目如何连接 Redis。
- Redis 高级:持久化、高可用、缓存治理总览。
- 持久化:RDB、AOF 的原理和取舍。
- 发布订阅:理解轻量广播和可靠 MQ 的区别。
- 集群:主从、哨兵、Cluster。
- 主从哨兵 Cluster 全过程原理:理解全量复制、增量复制、Sentinel 故障转移、slot、MOVED、ASK。
- 缓存问题:穿透、击穿、雪崩、热 Key、大 Key。
- 缓存一致性:MySQL 和 Redis 如何最终一致,删除失败、延迟双删、Binlog 补偿怎么做。
- 布隆过滤器原理:位数组、多哈希、误判率、缓存穿透商业落地。
- 大 Key 与热 Key 全过程治理:单次操作重、单 Key 热点、Cluster 热点、发现和治理方案。
- 高并发缓存治理:大 Key、热 Key、慢命令、连接池、限流降级、生产排查。
- 分布式锁:锁的正确写法、释放和续期风险。
- 红锁 Redlock:多 Redis 节点多数派加锁、适用边界和商业兜底。
- 分布式锁扩展点:公平锁、读写锁、信号量、Fencing Token、ZooKeeper/etcd 对比。
常见数据结构怎么选
| 类型 | 典型场景 | 示例 |
|---|---|---|
| String | 缓存、计数器、分布式锁 | set user:1 ... ex 3600 |
| Hash | 对象字段、购物车 | hset cart:1 sku:1 2 |
| List | 简单队列、时间线 | lpush queue item |
| Set | 去重、标签、共同好友 | sadd tag:java user:1 |
| ZSet | 排行榜、延迟队列 | zadd rank 100 user:1 |
| Bitmap | 签到、布尔状态统计 | setbit sign:202606 userId 1 |
| HyperLogLog | UV 估算 | pfadd uv:day user:1 |
选择数据结构时,不要只看“能不能存”,还要看“查询和更新方式”。
例如排行榜用 ZSet,不是因为 ZSet 比 List 高级,而是因为 ZSet 天然按 score 排序:
bash
zadd article:rank 100 article:1 80 article:2
zrevrange article:rank 0 9 withscores缓存读取流程
Redis 最常见用法是旁路缓存,也叫 Cache Aside。
mermaid
flowchart TD
A["请求数据"] --> B["查 Redis"]
B --> C{"命中缓存"}
C -- "是" --> D["返回缓存"]
C -- "否" --> E["查数据库"]
E --> F{"数据库有数据"}
F -- "有" --> G["写入 Redis,并设置 TTL"]
F -- "无" --> H["缓存空值或直接返回"]
G --> I["返回结果"]
H --> I为什么要设置 TTL:
- 避免旧数据永久存在。
- 避免内存被无效数据长期占用。
- 给缓存和数据库不一致提供自动恢复机会。
如果不设置过期时间,缓存可能长期占用内存,并且数据库数据更新后缓存一直是旧值。
命令 Demo
String 缓存:
bash
set user:profile:1001 '{"name":"Tom"}' ex 3600
get user:profile:1001
ttl user:profile:1001计数器:
bash
set article:read:1001 0
incr article:read:1001
incrby article:read:1001 10Hash 保存对象字段:
bash
hset cart:1001 product:1 2 product:2 1
hgetall cart:1001
hincrby cart:1001 product:1 1ZSet 排行榜:
bash
zadd rank:article 100 article:1 80 article:2
zincrby rank:article 20 article:2
zrevrange rank:article 0 9 withscores分布式锁基础写法:
bash
set lock:order:1001 token-uuid nx ex 30释放锁不能直接 del,必须判断 token 是否是自己持有的锁,具体看 分布式锁。
常见风险
| 风险 | 后果 | 处理方向 |
|---|---|---|
| 缓存穿透 | 大量无效请求打到数据库 | 缓存空值、布隆过滤器 |
| 缓存击穿 | 热点 Key 过期瞬间打爆数据库 | 互斥锁、逻辑过期 |
| 缓存雪崩 | 大量 Key 同时失效 | TTL 随机、预热、限流 |
| 热 Key | 单节点压力过大 | 本地缓存、拆 Key、限流 |
| 大 Key | 网络慢、删除阻塞、内存不均 | 拆分、分页、异步删除 |
| 数据不一致 | 用户看到旧数据 | 合理 TTL、删除缓存、异步补偿 |
| 锁误删 | 释放了别人的锁 | 唯一 token + Lua 脚本 |
使用建议
- Key 命名要有业务前缀,例如
user:profile:1001。 - 缓存数据尽量设置过期时间。
- 不要把 Redis 当成唯一可靠数据库,除非明确接受数据丢失风险并做好持久化。
- 大 Key 要拆分,避免阻塞 Redis。
- 热点 Key 要监控,必要时做本地缓存或分片。
- 分布式锁要设置过期时间,并使用唯一 token 释放锁。
- Redis 异常时要保护数据库,不能让所有流量直接打穿到 MySQL。
高并发下的大 Key、热 Key、慢命令、连接池耗尽和 Cluster 热点治理,继续看 高并发缓存治理。
学 Redis 的重点不是背命令,而是能判断一个场景是否适合 Redis,并知道它带来的数据一致性、内存、并发和可用性风险。
