Redis缓存雪崩:如何通过锁和TTL抖动来预防
了解过期缓存键的请求激增如何压垮数据库,以及如何在Redis中使用互斥锁、概率性提前重算和TTL抖动来阻止这种情况。
Redis缓存雪崩:如何通过锁和TTL抖动来预防
什么是缓存雪崩?
缓存雪崩是指大量缓存键在同一时间过期,导致所有请求直接打到数据库,造成数据库负载激增甚至崩溃的现象。
解决方案
1. 互斥锁(Mutex Lock)
使用Redis的SETNX命令实现互斥锁,确保只有一个请求去重建缓存:
import redis
r = redis.Redis()
def get_data(key):
# 尝试获取缓存
data = r.get(key)
if data is not None:
return data
# 尝试获取锁
lock_key = f"lock:{key}"
if r.setnx(lock_key, "1"):
# 设置锁过期时间,防止死锁
r.expire(lock_key, 10)
# 从数据库获取数据
data = query_database(key)
# 设置缓存
r.setex(key, 3600, data)
# 释放锁
r.delete(lock_key)
return data
else:
# 等待并重试
time.sleep(0.1)
return get_data(key)
2. 概率性提前重算
在缓存过期前,以一定概率提前重建缓存:
import random
def get_data_with_probability(key):
data = r.get(key)
if data is not None:
# 检查是否需要提前重算
ttl = r.ttl(key)
if ttl < 300 and random.random() < 0.1:
# 异步重建缓存
rebuild_cache_async(key)
return data
# 缓存不存在,正常获取
return rebuild_cache(key)
3. TTL抖动
给缓存过期时间添加随机值,避免大量键同时过期:
import random
def set_cache_with_jitter(key, data, base_ttl=3600):
# 添加±30%的随机抖动
jitter = random.uniform(0.7, 1.3)
ttl = int(base_ttl * jitter)
r.setex(key, ttl, data)
最佳实践
- 组合使用:将互斥锁和TTL抖动结合使用效果最佳
- 设置合理的过期时间:避免所有缓存同时过期
- 使用缓存预热:系统启动时预先加载热点数据
- 监控告警:实时监控缓存命中率和数据库负载
总结
缓存雪崩是分布式系统中常见的问题,通过互斥锁、概率性提前重算和TTL抖动可以有效预防。选择适合业务场景的方案,结合监控和告警机制,可以确保系统在高并发下的稳定性。