- 缓存失效时间打散
- 策略:为缓存设置不同的过期时间,避免大量缓存同时过期。例如,可以在原有的过期时间基础上,加上一个随机值(如1 - 10分钟的随机数)。这样,缓存过期时间就会分散开来,不会集中失效,从而降低缓存雪崩的风险。
- 示例代码(以Python和Redis为例):
import redis
import random
r = redis.Redis(host='localhost', port=6379, db = 0)
key = 'example_key'
value = 'example_value'
base_expire = 3600 # 基础过期时间3600秒(1小时)
random_expire = random.randint(60, 600) # 1 - 10分钟的随机数
total_expire = base_expire + random_expire
r.setex(key, total_expire, value)
- 使用互斥锁(Mutex)
- 策略:在缓存失效后,当第一个请求来重建缓存时,使用互斥锁(如Redis的SETNX命令)来保证只有一个线程去重建缓存,其他线程等待。当重建完成后,释放锁,其他线程可以直接从缓存中获取数据。这样可以防止大量请求同时去重建缓存,从而避免对后端数据库等数据源造成过大压力。
- 示例代码(以Python和Redis为例):
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
key = 'example_key'
mutex_key = 'example_mutex'
while True:
value = r.get(key)
if value:
break
if r.setnx(mutex_key, 1):
try:
# 重建缓存逻辑,例如从数据库读取数据
new_value = 'new_value_from_db'
r.setex(key, 3600, new_value)
finally:
r.delete(mutex_key)
break
else:
# 等待一段时间后重试
import time
time.sleep(0.1)
- 二级缓存
- 策略:采用二级缓存架构,例如设置一个本地缓存(如Guava Cache)作为一级缓存,Redis作为二级缓存。当请求到达时,先从本地缓存获取数据,如果本地缓存没有命中,再从Redis获取。如果Redis也没有命中,才去后端数据源获取数据,并依次更新两级缓存。这样即使Redis中的大量缓存失效,本地缓存还能在一定程度上提供数据,减轻后端压力。
- 示例代码(以Java和Guava Cache、Redis为例):
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import redis.clients.jedis.Jedis;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class DoubleCacheExample {
private static LoadingCache<String, String> localCache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
return null;
}
});
private static Jedis jedis = new Jedis("localhost", 6379);
public static String getValue(String key) {
String value = localCache.getIfPresent(key);
if (value == null) {
value = jedis.get(key);
if (value != null) {
localCache.put(key, value);
} else {
// 从后端数据源获取数据
value = "value_from_backend";
jedis.setex(key, 3600, value);
localCache.put(key, value);
}
}
return value;
}
}
- 缓存持久化
- 策略:对于Redis等缓存系统,启用持久化机制(如RDB或AOF)。这样在系统重启时,可以快速恢复缓存数据,避免缓存全部失效的情况。RDB会在指定的时间间隔内将内存中的数据集快照写入磁盘,AOF则是将每次写操作追加到文件末尾。可以根据实际需求选择合适的持久化方式或两者结合使用。
- 配置示例:
- RDB配置:在redis.conf文件中,设置
save 900 1
表示900秒内如果有1个键被修改,则进行RDB快照。
- AOF配置:在redis.conf文件中,设置
appendonly yes
开启AOF持久化,并可以设置appendfsync everysec
表示每秒将缓冲区内容写入AOF文件。
- 服务降级与熔断
- 策略:当缓存雪崩发生,系统压力过大时,启用服务降级策略。例如对于一些非核心业务,可以直接返回默认值或者提示信息,避免占用过多资源。同时,结合熔断机制,当对后端数据源的请求失败率达到一定阈值时,暂时切断对数据源的请求,直接返回降级结果,防止对数据源的进一步压力,待系统恢复稳定后再尝试恢复正常请求。
- 示例(以Hystrix框架为例):
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
public class FallbackCommand extends HystrixCommand<String> {
private final String key;
public FallbackCommand(String key) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.key = key;
}
@Override
protected String run() throws Exception {
// 正常从数据源获取数据逻辑
return "data_from_backend";
}
@Override
protected String getFallback() {
// 降级逻辑,返回默认值
return "default_value";
}
}
- 增加缓存副本
- 策略:对一些重要的缓存数据,创建多个副本,并将这些副本分布在不同的缓存节点上。当某个节点的缓存失效时,其他节点的副本可以继续提供服务,从而提高系统的可用性。可以通过一致性哈希算法等方式来管理缓存副本的分布。
- 示例:在使用Redis Cluster时,可以通过配置副本数量来实现缓存副本。例如在集群配置文件中设置
cluster - replicas 1
表示每个主节点有1个副本节点。这样当主节点缓存失效时,副本节点可以继续提供服务。