本文目录导读:

下面给你一套 Java 面试中关于 Redis 的高频案例讲解,从简单到深入,覆盖缓存穿透、击穿、雪崩、分布式锁、数据一致性等核心场景,并附上代码示例和回答话术。
高频案例:缓存穿透 / 击穿 / 雪崩
这是面试官必问的“三兄弟”,核心考察你对缓存异常场景的理解和解决方案。
缓存穿透(查不存在的数据)
现象:大量请求查询一个缓存和数据库都不存在的 key,导致请求直接打到数据库。
解决方案:
- 缓存空值:如果数据库查不到,也把空值缓存起来,设置较短的过期时间(如 5 分钟)。
- 布隆过滤器:在缓存之前加一层 Bloom Filter,拦截不存在的 key。
代码示例(缓存空值):
public String getData(String key) {
// 1. 查缓存
String value = redis.get(key);
if (value != null) {
return value;
}
// 2. 缓存未命中,查数据库
String dbValue = queryDB(key);
if (dbValue == null) {
// 3. 缓存空值,防止穿透
redis.set(key, "", 300); // 5分钟过期
return null;
}
// 4. 写回缓存
redis.set(key, dbValue, 3600);
return dbValue;
}
缓存击穿(热点 key 过期)
现象:一个热点 key 在过期瞬间,大量并发请求同时打向数据库。
解决方案:
- 互斥锁:只允许一个线程去重建缓存,其他线程等待。
- 逻辑过期:不设置物理过期时间,而是存一个逻辑过期时间,异步更新。
代码示例(互斥锁):
public String getDataWithLock(String key) {
String value = redis.get(key);
if (value != null) {
return value;
}
// 加锁(使用 Redis SETNX 实现)
String lockKey = "lock:" + key;
boolean locked = redis.setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS);
if (locked) {
try {
// 二次检查(double check)
value = redis.get(key);
if (value != null) return value;
// 查数据库
value = queryDB(key);
redis.set(key, value, 3600);
return value;
} finally {
redis.delete(lockKey);
}
} else {
// 等待重试
Thread.sleep(100);
return getDataWithLock(key); // 递归重试
}
}
缓存雪崩(大量 key 同时过期)
现象:大量 key 在同一时间过期,导致请求全部打到数据库。
解决方案:
- 随机过期时间:在设置过期时间时加上随机值。
- 多级缓存:本地缓存(Caffeine)+ Redis。
- 熔断降级:数据库压力过大时,直接返回默认值。
// 设置过期时间时加随机值 int expireTime = 3600 + new Random().nextInt(600); // 1小时 + 0~10分钟随机 redis.set(key, value, expireTime);
分布式锁(重点考察)
场景
在分布式系统中,多个服务同时操作同一个资源(如库存扣减),需要保证互斥性。
要求
- 互斥性:同一时刻只有一个线程能获取锁。
- 防死锁:锁必须设置过期时间。
- 防误删:只能删除自己的锁(加 value 标识)。
代码示例(Redisson 实现)
@Autowired
private RedissonClient redissonClient;
public void deductStock() {
RLock lock = redissonClient.getLock("stock:1001");
try {
// 尝试加锁,等待 10 秒,锁有效 30 秒
if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
// 业务逻辑:扣减库存
int stock = getStock();
if (stock > 0) {
reduceStock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
}
}
面试关键点:
- 为什么不用 SETNX 自己实现?—— Redisson 有watch dog 自动续期、可重入等特性。
- 锁过期时间设置多少合适?—— 要结合业务执行时间,或使用 Redisson 自动续期。
缓存与数据库双写一致性
场景
更新数据库后,如何保证缓存一致?
方案对比
| 方案 | 说明 | 问题 |
|---|---|---|
| 先更新数据库,再删缓存 | 推荐 | 可能短暂不一致,但最终一致 |
| 先删缓存,再更新数据库 | 不推荐 | 并发下容易脏数据 |
| 延迟双删 | 先删缓存 → 更新数据库 → 延迟再删 | 解决并发问题 |
推荐方案(先更新 DB,再删缓存)
@Transactional
public void updateData(String id, String newValue) {
// 1. 更新数据库
database.update(id, newValue);
// 2. 删除缓存
redis.delete("key:" + id);
}
补充(延迟双删):
public void updateData(String id, String newValue) {
redis.delete("key:" + id); // 先删缓存
database.update(id, newValue);
// 延迟 500ms 再删一次
Thread.sleep(500);
redis.delete("key:" + id);
}
热点数据缓存(商品详情)
场景
商品详情页高并发访问量巨大,如何设计?
方案
- 多级缓存:本地缓存(Caffeine)→ Redis → 数据库。
- 缓存预热:项目启动时,把热门商品加载到缓存。
- 缓存更新策略:定时刷新 + 主动失效。
// 缓存预热
@Component
public class CachePreload implements ApplicationRunner {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public void run(ApplicationArguments args) {
List<Product> hotProducts = productService.getHotProducts();
for (Product product : hotProducts) {
redisTemplate.opsForValue().set("product:" + product.getId(), product, 1, TimeUnit.HOURS);
}
}
}
排行榜(ZSet 应用)
场景
游戏排行榜、销售榜单等。
代码示例
// 更新分数
redisTemplate.opsForZSet().add("rank", "user:1001", 85.5);
// 获取 Top 10
Set<Object> top10 = redisTemplate.opsForZSet().reverseRange("rank", 0, 9);
// 获取某用户排名
Long rank = redisTemplate.opsForZSet().reverseRank("rank", "user:1001");
面试回答话术模板
面试官问:“你们项目中 Redis 是怎么用的?”
建议从以下三方面回答:
缓存场景:商品详情缓存、用户信息缓存,使用 Spring Cache + Redis。 2. 分布式锁:库存扣减时使用 Redisson 分布式锁防止超卖。 3. 排行榜/计数器:点赞用 incr,排行榜用 ZSet。
然后引出经典问题:
“在缓存使用过程中,我们也考虑到了缓存穿透、击穿、雪崩。 穿透我们用缓存空值+布隆过滤器; 击穿我们用互斥锁; 雪崩用过期时间加随机值。 对于一致性,我们采用先更新数据库再删缓存的策略,最终一致即可。”
备战建议
| 知识点 | 必备程度 | 面试频率 |
|---|---|---|
| 缓存穿透/击穿/雪崩 | 90% | |
| 分布式锁 | 80% | |
| 缓存一致性 | 70% | |
| ZSet 排行榜 | 40% | |
| 持久化机制(RDB/AOF) | 50% | |
| 过期删除策略 | 30% |
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播