本文目录导读:

缓存穿透是指查询一个根本不存在的数据,缓存层(如 Redis)和存储层(如数据库)都没有,导致每次请求都穿透到数据库,失去缓存保护。
面试或写代码时,实现的思路通常是“模拟”穿透,而不是“实现”穿透——那是系统设计要避免的。
既然你问“怎么实现”,可能是想写一个小 Demo 来演示这种场景,然后验证布隆过滤器或空值缓存等解决方案。
下面给出一个 Java 案例,演示缓存穿透的发生与解决。
模拟缓存穿透(出现)
场景:
查用户信息,缓存用 Map 模拟(或 Redis),数据库用 List 模拟。
查一个不存在的 ID(-1),每次都会查库。
import java.util.HashMap;
import java.util.Map;
public class CachePenetrationDemo {
// 模拟缓存
private Map<String, String> cache = new HashMap<>();
// 模拟数据库
private Map<String, String> db = new HashMap<>();
public CachePenetrationDemo() {
db.put("1", "张三");
db.put("2", "李四");
}
// 没有穿透防护
public String getUserNoProtect(String id) {
// 1. 查缓存
String name = cache.get(id);
if (name != null) {
System.out.println("缓存命中: " + name);
return name;
}
// 2. 查数据库
System.out.println("缓存未命中,查询数据库...");
name = db.get(id);
if (name != null) {
cache.put(id, name); // 放入缓存
}
return name;
}
public static void main(String[] args) {
CachePenetrationDemo demo = new CachePenetrationDemo();
// 多次查询不存在的 id
System.out.println("===== 缓存穿透演示 =====");
System.out.println(demo.getUserNoProtect("-1")); // 查库
System.out.println(demo.getUserNoProtect("-1")); // 仍然查库!因为没有缓存
System.out.println(demo.getUserNoProtect("-1")); // 每次都是库
}
}
输出结果:
缓存未命中,查询数据库...
null
缓存未命中,查询数据库...
null
缓存未命中,查询数据库...
null
每次请求都落到数据库,这就是 缓存穿透。
解决方案一:缓存空对象(Null Cache)
思路:查询不存在的数据时,缓存一个空值(或特殊标记),设置较短的过期时间。
// 防止穿透:缓存空对象
public String getUserWithNullCache(String id) {
// 1. 查缓存
String name = cache.get(id);
if (name != null) {
System.out.println("缓存命中: " + name);
return "null".equals(name) ? null : name;
}
// 2. 查数据库
System.out.println("缓存未命中,查询数据库...");
name = db.get(id);
if (name != null) {
cache.put(id, name); // 缓存正常值
} else {
cache.put(id, "null"); // 缓存空标记(过期时间短)
}
return name;
}
测试:
System.out.println("===== 空值缓存保护 =====");
System.out.println(demo.getUserWithNullCache("-1")); // 查库,缓存 null
System.out.println(demo.getUserWithNullCache("-1")); // 缓存命中,返回 null
输出(第二次不再查库):
缓存未命中,查询数据库...
null
缓存命中: null ← 直接返回,不查库
解决方案二:布隆过滤器(Bloom Filter)
在查询前先判断是否存在,不存在则直接返回,存在才查缓存/库。
import java.util.BitSet;
// 简单的布隆过滤器实现(仅用于演示)
class BloomFilter {
private BitSet bits = new BitSet(1 << 24);
private int[] seeds = {3, 5, 7, 11, 13, 17};
private int hash(String key, int seed) {
int h = 0;
for (char c : key.toCharArray()) {
h = h * seed + c;
}
// 取模到 bits 大小
return Math.abs(h % bits.size());
}
public void add(String key) {
for (int seed : seeds) {
bits.set(hash(key, seed));
}
}
public boolean mightContain(String key) {
for (int seed : seeds) {
if (!bits.get(hash(key, seed))) {
return false; // 肯定不存在
}
}
return true; // 可能存在
}
}
// 使用布隆过滤器保护
BloomFilter bloom = new BloomFilter();
// 初始化:把所有合法 ID 加入布隆过滤器
{
bloom.add("1");
bloom.add("2");
}
public String getUserWithBloom(String id) {
// 1. 布隆过滤器快速判断
if (!bloom.mightContain(id)) {
System.out.println("布隆过滤器判定不存在,直接返回");
return null;
}
// 2. 查缓存
String name = cache.get(id);
if (name != null) {
System.out.println("缓存命中: " + name);
return "null".equals(name) ? null : name;
}
// 3. 查库
System.out.println("查数据库...");
name = db.get(id);
if (name != null) {
cache.put(id, name);
} else {
cache.put(id, "null");
}
return name;
}
测试:
System.out.println("===== 布隆过滤器保护 =====");
System.out.println(demo.getUserWithBloom("-1")); // 直接返回,不查库也不查缓存
System.out.println(demo.getUserWithBloom("1")); // 正常查缓存
关键点总结
| 方案 | 原理 | 缺点 | 适合场景 |
|---|---|---|---|
| 缓存空对象 | 不存在的数据也缓存,设短过期时间 | 占用缓存空间,不一致窗口 | 穿透量不大 |
| 布隆过滤器 | 提前判断 key 是否存在 | 有误判率(会把存在判为不存在) | 大量穿透攻击 |
如果你想要的是 纯“实现穿透”代码(即故意制造穿透),直接看第一个 getUserNoProtect 方法即可——它没有任何防护,每次不存在就查库,这就是缓存穿透的表现。
在真实项目中,你通常要做的是:认出它,然后加上空值缓存或布隆过滤器。
版权声明: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直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播