Redis 缓存实战 - 从入门到高可用
缓存穿透、击穿、雪崩怎么办?本文全面讲解 Redis 缓存实战和高可用方案。
折
折腾侠
2026/03/15 发布
17约 6 分钟959 字 / 695 词00
Redis 缓存实战 - 从入门到高可用
缓存穿透、击穿、雪崩怎么办?本文全面讲解 Redis 缓存实战和高可用方案。
📋 前言
Redis 是现代架构的标配。
常见场景:
- 缓存热点数据
- 分布式锁
- 会话存储
- 消息队列
- 排行榜
常见问题:
- 缓存穿透、击穿、雪崩
- 数据一致性
- 内存溢出
- 高可用方案
本文从基础到高可用,全面讲解 Redis 实战。
🎯 Redis 基础
数据类型
| 类型 | 用途 | 示例 |
|---|---|---|
| String | 缓存、计数器 | 用户信息、访问次数 |
| List | 消息队列、最新列表 | 消息队列、最新文章 |
| Hash | 存储对象 | 用户详情、商品详情 |
| Set | 去重、共同好友 | 标签、好友关系 |
| ZSet | 排行榜 | 热度排行、分数排行 |
常用命令
Bash
# String
SET key value
GET key
INCR key # 自增
SETEX key seconds value # 带过期时间
# Hash
HSET user:1 name "张三"
HGET user:1 name
HGETALL user:1
# List
LPUSH queue "message"
RPOP queue
# Set
SADD tags "java"
SMEMBERS tags
# ZSet
ZADD leaderboard 100 "user1"
ZREVRANGE leaderboard 0 9
⚡ 缓存三大问题
1. 缓存穿透
问题: 查询不存在的数据,缓存不命中,直接打到数据库。
解决方案:
Java
// 方案 1:缓存空值
public User getUser(String id) {
User user = redis.get(id);
if (user != null) {
return user;
}
// 查询数据库
user = db.query(id);
if (user == null) {
// 缓存空值,防止重复查询
redis.setex(id, 60, null);
return null;
}
redis.setex(id, 3600, user);
return user;
}
// 方案 2:布隆过滤器
if (!bloomFilter.mightContain(id)) {
return null; // 肯定不存在
}
2. 缓存击穿
问题: 热点 key 过期瞬间,大量请求打到数据库。
解决方案:
Java
// 方案 1:互斥锁
public User getUser(String id) {
User user = redis.get(id);
if (user != null) {
return user;
}
// 获取分布式锁
if (tryLock(id)) {
try {
// 双重检查
user = redis.get(id);
if (user != null) {
return user;
}
user = db.query(id);
redis.setex(id, 3600, user);
} finally {
unlock(id);
}
} else {
// 等待重试
Thread.sleep(100);
return getUser(id);
}
}
// 方案 2:永不过期 + 异步更新
// 逻辑过期时间,后台线程异步更新
3. 缓存雪崩
问题: 大量 key 同时过期,或 Redis 宕机,请求全部打到数据库。
解决方案:
Java
// 方案 1:随机过期时间
int expireTime = 3600 + new Random().nextInt(600);
redis.setex(key, expireTime, value);
// 方案 2:多级缓存
// 本地缓存 + Redis + 数据库
// 方案 3:高可用架构
// 主从复制 + 哨兵 + 集群
🔄 数据一致性
缓存更新策略
方案 1:先删缓存,再更新数据库
Java
// ❌ 有问题
redis.delete(key);
db.update(data);
// 问题:更新数据库时,其他请求可能读到旧数据并写入缓存
方案 2:先更新数据库,再删缓存
Java
// ✅ 推荐
db.update(data);
redis.delete(key);
// 问题:极端情况下仍有不一致
方案 3:延迟双删
Java
// ✅ 更可靠
db.update(data);
redis.delete(key);
Thread.sleep(500);
redis.delete(key);
方案 4:监听 binlog 异步删除
Java
// ✅ 最终一致性
// Canal 监听 MySQL binlog
// 异步删除 Redis 缓存
🏗️ 高可用架构
1. 主从复制
Master (写)
↓ 复制
Slave (读)
特点:
- 读写分离
- 数据备份
- 从机故障不影响
- 主机故障需手动切换
2. 哨兵模式
Sentinel 1
Sentinel 2
Sentinel 3
↓ 监控
Master + Slaves
特点:
- 自动故障转移
- 监控告警
- 至少 3 个哨兵
- 推荐 1 主 2 从 3 哨兵
3. 集群模式
Node 1 (0-5460)
Node 2 (5461-10922)
Node 3 (10923-16383)
特点:
- 数据分片
- 高可用
- 水平扩展
- Redis 3.0+ 支持
💡 实战场景
场景 1:热点数据缓存
Java
@Cacheable(value = "user", key = "#id",
unless = "#result == null",
cacheManager = "redisCacheManager")
public User getUser(String id) {
return userRepository.findById(id);
}
场景 2:分布式锁
Java
// Redisson 实现
RLock lock = redisson.getLock("order:" + orderId);
if (lock.tryLock(0, 30, TimeUnit.SECONDS)) {
try {
// 业务逻辑
createOrder(order);
} finally {
lock.unlock();
}
}
场景 3:限流
Java
// 固定窗口
String key = "limit:" + userId + ":" + System.currentTimeMillis() / 60000;
long count = redis.incr(key);
if (count == 1) {
redis.expire(key, 60);
}
if (count > 100) {
throw new RateLimitException();
}
// 滑动窗口(推荐)
// 使用 ZSet 实现
场景 4:排行榜
Java
// 添加分数
redis.zadd("leaderboard", score, userId);
// 获取前 10 名
Set<String> top10 = redis.zrevrange("leaderboard", 0, 9);
// 获取用户排名
Long rank = redis.zrank("leaderboard", userId);
场景 5:消息队列
Java
// 生产者
redis.lpush("queue", message);
// 消费者
String message = redis.brpop(0, "queue");
// 发布订阅
redis.publish("channel", message);
redis.subscribe("channel");
⚠️ 常见问题
1. 内存溢出
原因:
- 缓存太多数据
- 没有设置过期时间
- 大 key 问题
解决方案:
Bash
# 设置内存淘汰策略
CONFIG SET maxmemory-policy allkeys-lru
# 查找大 key
redis-cli --bigkeys
# 定期清理
UNLINK key # 异步删除
2. 热 key 问题
识别:
Bash
# 监控命令
MONITOR
SLOWLOG GET 10
解决:
- 本地缓存
- 复制多份(热 key_1, 热 key_2)
- 限流
3. 大 key 问题
识别:
Bash
redis-cli --bigkeys
解决:
- 拆分大 key
- 异步删除
- 避免一次性操作
🎯 最佳实践
命名规范
Bash
# 格式:业务:类型:ID
user:info:123
order:detail:456
product:stock:789
# 使用冒号分隔
# 便于理解和监控
过期时间
Java
// 基础时间 + 随机值
int baseTime = 3600;
int randomTime = new Random().nextInt(600);
redis.setex(key, baseTime + randomTime, value);
监控告警
Bash
# 关键指标
- 内存使用率
- QPS
- 命中率
- 连接数
- 慢查询
# 工具
- Redis Monitor
- Prometheus + Grafana
🎁 总结
核心要点:
✅ 理解五大数据类型
✅ 掌握缓存三大问题解决方案
✅ 了解高可用架构
✅ 注意数据一致性
✅ 做好监控告警
架构选型:
小型项目:单机 + 持久化
中型项目:主从 + 哨兵
大型项目:集群 + 多活
最后建议:
缓存是银弹,但不是万能药。
理解场景,合理设计,持续优化。
你有什么 Redis 实战经验? 欢迎在评论区分享!👇
📚 参考资源
- Redis 官方文档
- 《Redis 设计与实现》
- 《Redis 实战》