缓存击穿发生在高并发查询某个不存在或刚过期的热点Key时,大量请求直接穿透缓存冲击数据库。本文将为你构建一套七层防御体系,彻底驯服这只猛兽。
第一层护甲:空值缓存(英勇黄铜)
核心思想:即使数据库查无数据,也缓存空结果(如NULL
或特殊标记),避免反复穿透。
// 伪代码示例:缓存空值策略
public Product getProduct(String id) {
// 1. 尝试从Redis获取
String cacheKey = "product:" + id;
Product product = redis.get(cacheKey, Product.class);
if (product != null) {
// 2. 命中空值标记(如"NULL")直接返回
if ("NULL".equals(product.getSpecialFlag()))
return null;
return product;
}
// 3. 查数据库
product = db.query("SELECT * FROM products WHERE id = ?", id);
if (product == null) {
// 4. 数据库无数据,缓存空值(5分钟过期)
redis.setex(cacheKey, 300, "NULL");
} else {
// 5. 数据库有数据,正常缓存
redis.setex(cacheKey, 3600, product);
}
return product;
}
第二层护甲:布隆过滤器(不屈白银)
核心思想:在缓存访问前增加一层过滤器,拦截绝对不存在的Key请求。
# Python使用pybloom库示例
from pybloom import ScalableBloomFilter
# 初始化可扩容布隆过滤器
bloom_filter = ScalableBloomFilter(initial_capacity=1000)
# 预热:将有效ID加入过滤器
valid_ids = ["prod_1001", "prod_1002", "prod_1005"]
for id in valid_ids:
bloom_filter.add(id)
# 查询请求处理
def handle_request(product_id):
if product_id not in bloom_filter:
return "Invalid ID" # 快速拦截非法请求
# ...继续后续缓存查询逻辑
第三层护甲:互斥锁(华贵铂金)
核心思想:当缓存失效时,仅允许一个线程重建缓存,其他线程等待或轮询。
// 基于Redis分布式锁的实现
public Product getProductWithLock(String id) {
String cacheKey = "product:" + id;
Product product = redis.get(cacheKey, Product.class);
if (product != null) return product;
// 尝试获取分布式锁 (SET key value NX EX 实现原子操作)
String lockKey = "lock:product:" + id;
String clientId = UUID.randomUUID().toString(); // 唯一标识当前实例
boolean locked = redis.set(lockKey, clientId, "NX", "EX", 5); // 锁5秒超时
if (locked) {
try {
// 再次检查缓存(防止其他线程已更新)
product = redis.get(cacheKey, Product.class);
if (product != null) return product;
// 查询数据库并重建缓存
product = db.queryProduct(id);
redis.setex(cacheKey, 3600, product);
} finally {
// 使用Lua脚本保证原子性释放锁
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else return 0 end";
redis.eval(luaScript, 1, lockKey, clientId);
}
} else {
// 未获锁,短暂睡眠后重试
Thread.sleep(100);
return getProductWithLock(id); // 递归重试(需注意深度)
}
return product;
}
第四层护甲:热点数据永不过期(璀璨钻石)
核心思想:对极端热点Key,设置逻辑过期时间而非物理过期,异步刷新缓存。
// 逻辑过期时间示例
public class ProductWrapper {
private Product product;
private long expireTime; // 逻辑过期时间戳
// 构造函数、getter、setter省略
}
public Product getHotProduct(String id) {
ProductWrapper wrapper = redis.get("hot:"+id, ProductWrapper.class);
if (System.currentTimeMillis() < wrapper.getExpireTime()) {
return wrapper.getProduct(); // 未过期直接返回
}
// 触发异步更新(如提交到线程池)
threadPool.submit(() -> rebuildHotProductCache(id));
return wrapper.getProduct(); // 仍返回旧数据
}
private void rebuildHotProductCache(String id) {
// 获取最新数据,更新缓存(带新过期时间)
Product newProd = db.queryProduct(id);
ProductWrapper newWrapper = new ProductWrapper(newProd,
System.currentTimeMillis() + 3600_000); // 1小时后过期
redis.set("hot:"+id, newWrapper);
}
第五层护甲:熔断降级(超凡大师)
核心思想:当数据库压力过大时,主动熔断部分请求,返回兜底数据保护系统。
// 使用Resilience4j熔断器示例
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("dbCircuit");
public Product getProductWithCircuitBreaker(String id) {
return circuitBreaker.executeSupplier(() -> {
// 尝试走正常缓存/数据库逻辑
return getProduct(id);
});
}
// 配置熔断规则(当失败率>50%时熔断10秒)
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofSeconds(10))
.build();
第六层护甲:多级缓存(傲世宗师)
核心思想:在Redis前增加本地缓存(如Caffeine),进一步减少穿透概率。
// Caffeine + Redis 二级缓存
LoadingCache<String, Product> localCache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(30, TimeUnit.SECONDS)
.build(key -> {
// 当本地缓存未命中,从Redis加载
Product p = redis.get("product:"+key, Product.class);
if (p == null) {
p = db.queryProduct(key); // Redis没有则查库
redis.setex("product:"+key, 3600, p);
}
return p;
});
// 最终获取产品方法
public Product getProductMultiLevel(String id) {
return localCache.get(id);
}
第七层护甲:请求合并(最强王者)
核心思想:将短时间内对同一Key的多次请求合并为一次数据库查询。
// 使用Guava的LoadingCache合并请求
LoadingCache<String, Product> requestMergerCache = Caffeine.newBuilder()
.maximumSize(1000)
.build(new CacheLoader<String, Product>() {
@Override
public Product load(String key) {
// 真实查询数据库(多个线程会等待此结果)
return db.queryProduct(key);
}
});
public Product getProductMerged(String id) {
try {
return requestMergerCache.get(id);
} catch (Exception e) {
// 异常处理
}
}
防御体系全景图(思维导图)
实战总结
七层防御体系并非需要全量部署,而是根据业务场景灵活组合:
- 基础必选:空值缓存 + 互斥锁(90%场景适用)
- 热点场景:布隆过滤器 + 永不过期策略(比如明星婚讯、秒杀商品)
- 系统加固:熔断降级 + 多级缓存(高并发金融系统)
- 极致优化:请求合并(长尾请求优化)