某社交平台在明星官宣离婚时突发崩溃:每秒50万查询涌向数据库,导致核心服务不可用30分钟。事后分析发现,恶意用户伪造海量不存在的用户ID发起请求,同时大量热点Key集中失效,引发缓存穿透与雪崩的双重风暴。这个千万级损失的案例,揭示了缓存异常处理的生死攸关。
危害实测(4核8G MySQL实例):
请求量 | 正常QPS | 穿透攻击下QPS | 数据库负载 |
---|---|---|---|
10,000 | 2,500 | 1,200 | 65% |
100,000 | 2,300 | 崩溃 | 100% |
方案1:空值缓存 - 简易应急方案
public Object getData(String key) {
// 1. 查缓存
Object value = redis.get(key);
if (value != null) {
if ("NULL".equals(value)) return null; // 识别空值
return value;
}
// 2. 查数据库
value = db.query(key);
if (value == null) {
// 3. 缓存空值(短时间)
redis.setex(key, 60, "NULL"); // 60秒过期
return null;
}
// 4. 正常缓存数据
redis.setex(key, 300, value); // 5分钟缓存
return value;
}
优缺点:
方案2:布隆过滤器 - 企业级防护盾
// 初始化布隆过滤器
RedissonClient redisson = Redisson.create();
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("userFilter");
bloomFilter.tryInit(10000000, 0.01); // 1000万数据,1%误判率
// 数据写入时
public void addUser(User user) {
db.insert(user);
bloomFilter.add(user.getId()); // 添加到过滤器
}
// 查询处理
public User getUser(String id) {
// 1. 布隆过滤器拦截
if (!bloomFilter.contains(id)) {
return null; // 肯定不存在
}
// 2. 正常查询流程
return getData(id);
}
性能基准:
数据规模 | 内存占用 | 误判率 | 查询性能 |
---|---|---|---|
100万 | 1.14 MB | 1% | 0.05ms |
1000万 | 11.4 MB | 1% | 0.08ms |
1亿 | 114 MB | 1% | 0.15ms |
方案3:请求检测 - 前置防火墙
# 基于规则的恶意请求拦截
def validate_request(request):
user_id = request.params.get('user_id')
# 规则1: ID格式校验
if not re.match(r'^[a-zA-Z0-9]{6,20}$', user_id):
return False
# 规则2: 频率限制
key = f"req_limit:{request.ip}:{user_id}"
if redis.incr(key) > 100: # 每秒100次
return False
redis.expire(key, 1)
# 规则3: 黑名单检测
if redis.sismember("blacklist", request.ip):
return False
return True
# 请求处理入口
@app.route('/user')
def get_user():
if not validate_request(request):
return "Invalid request", 403 # 拦截
# ...正常处理
场景1:批量Key同时失效
场景2:Redis集群宕机
防御层1:避免集中过期
// 差异化过期时间策略
public void setCache(String key, Object value) {
int baseTtl = 3600; // 基础1小时
int randomTtl = ThreadLocalRandom.current().nextInt(600); // 随机0-10分钟
redis.setex(key, baseTtl + randomTtl, value);
}
防御层2:服务降级与熔断
// 基于Hystrix的服务降级
@HystrixCommand(
fallbackMethod = "fallbackGetProduct",
commandProperties = {
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold", value="10"),
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds", value="5000")
}
)
public Product getProduct(String id) {
// ...正常查询
}
public Product fallbackGetProduct(String id) {
// 返回兜底数据
return new Product("默认商品", 0);
}
防御层3:高可用集群架构
# Redis哨兵配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
防御层4:请求限流
# Nginx限流配置
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
server {
location /api/ {
limit_req zone=api_limit burst=50 nodelay;
proxy_pass http://backend;
}
}
场景 | 穿透防护 | 雪崩防护 | 推荐工具 |
---|---|---|---|
用户查询 | 布隆过滤器 | 熔断降级 | RedisBloom + Hystrix |
商品信息 | 空值缓存 | 随机过期 | Redis |
配置数据 | 请求校验 | 永不过期+定时刷新 | Nginx + Spring Scheduler |
秒杀库存 | 布隆过滤器 | 限流排队 | RedisBloom + Sentinel |
# Prometheus监控配置
- name: cache_penetration
rules:
- alert: HighCachePenetration
expr: rate(redis_penetration_requests[5m]) > 1000
labels: severity: critical
- name: cache_snow_avalanche
rules:
- alert: CacheMassExpiration
expr: count(redis_keys_expiring_in_10m) > 10000
labels: severity: warning
- name: redis_down
rules:
- alert: RedisInstanceDown
expr: up{job="redis"} == 0
labels: severity: critical
背景:某电商平台用户服务:
优化方案:
# 初始化10亿用户布隆过滤器
BF.RESERVE user:global 0.01 1000000000
// 基础1小时 + 随机10分钟
int ttl = 3600 + ThreadLocalRandom.current().nextInt(600);
# Hystrix配置
hystrix.command.default.circuitBreaker.requestVolumeThreshold=20
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=10000
# Redis配置
cluster-enabled yes
cluster-node-timeout 5000
优化效果:
指标 | 优化前 | 优化后 | 提升 |
---|---|---|---|
数据库负载峰值 | 100% | 35% | 65%↓ |
缓存穿透拦截率 | 0% | 99.99% | 近100%↑ |
故障恢复时间 | >30分钟 | <30秒 | 60倍↑ |
错误率 | 15% | 0.01% | 99.9%↓ |
陷阱1:布隆过滤器误判导致数据丢失
// 二次验证机制
if (!bloomFilter.mightContain(userId)) {
// 记录日志并人工审核
log.warn("Bloomfilter reject: {}", userId);
// 特殊通道验证
if (specialVerify(userId)) {
bloomFilter.add(userId); // 加入过滤器
return getData(userId);
}
return null;
}
陷阱2:空值缓存内存爆炸
# 限制空值内存占用
MAXMEMORY 4GB
MAXMEMORY-POLICY allkeys-lru
陷阱3:熔断过度导致服务不可用
// 基于QPS动态调整
if (currentQps > 10000) {
hystrixConfig.setThreshold(50); // 提高阈值
} else {
hystrixConfig.setThreshold(20);
}
穿透防护三件套:
雪崩防御四重奏:
运维监控三板斧:
某金融系统采用本文方案后,在双十一期间成功抵御每秒12万次恶意请求,数据库负载稳定在40%以下,保障了十亿级交易顺利完成。
缓存穿透与雪崩的防御核心在于:
三条黄金法则: