Redis缓存雪崩解决方案

  1、什么是缓存雪崩?你有什么解决方案来防止缓存雪崩?
 
            如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。
            由于原有缓存失效,新缓存未到期间所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU 和内存造成巨大压力,严重的会造成数据库宕机
 

  2. 防止缓存雪崩解决方案

   

 1、加锁排队   key: whiltList  value:1000w个uid 指定setNx whiltList value nullValue
                mutex互斥锁解决,Redis的SETNX去set一个mutex key,
                当操作返回成功时,再进行load db的操作并回设缓存;
                否则,就重试整个get缓存的方法


            2、数据预热
                缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,
                先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
                可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key     


            3、双层缓存策略
                C1为原始缓存,C2为拷贝缓存,C1失效时,可以访问C2,C1缓存失效时间设置为短期,C2设置为长期。


            
            4、定时更新缓存策略
                失效性要求不高的缓存,容器启动初始化加载,采用定时任务更新或移除缓存


            5、设置不同的过期时间,让缓存失效的时间点尽量均匀
 

### Java 实现 Redis 缓存雪崩解决方案 缓存雪崩是指大量缓存数据在同一时间过期,导致请求直接打到数据库上,造成数据库压力骤增。为了避免这种情况发生,可以通过多种方式来缓解这个问题。 #### 方法一:设置不同的过期时间 通过给不同键设置随机的过期时间,可以避免所有缓存同时失效的情况。这可以在一定程度上减少因缓存集中失效带来的冲击[^1]。 ```java public class RedisCacheUtil { private static final int MIN_EXPIRE_TIME = 60; // 单位秒 private static final int MAX_EXPIRE_TIME = 300; /** * 设置带有随机过期时间的数据到Redis中 */ public void setWithRandomExpireTime(String key, String value) { Random random = new Random(); int expireSeconds = random.nextInt(MAX_EXPIRE_TIME - MIN_EXPIRE_TIME + 1) + MIN_EXPIRE_TIME; Jedis jedis = getJedisConnection(); // 获取连接方法需自行实现 try { jedis.setex(key, expireSeconds, value); } finally { closeJedis(jedis); // 关闭资源的方法也需自行实现 } } } ``` #### 方法二:加锁机制防止击穿 当某个热点key即将过期时,先尝试获取分布式锁再更新该key对应的value值并重置其TTL(生存周期)。这样即使有多个线程试图刷新同一个key也不会引发连锁反应[^2]。 ```java public boolean refreshKeyIfExpired(String key){ Jedis jedis = null; try{ jedis = pool.getResource(); Long ttl = jedis.ttl(key); if(ttl!=null && ttl<=0){ // 如果ttl小于等于零则表示已经到期或者不存在此key // 尝试获得锁 String lockKey = "lock:" + key; long acquireLockTimeoutMs = 5_000L; if(!jedis.setnx(lockKey,"locked")){ return false; // 已经被其他客户端锁定,则返回false不处理 } // 加入超时控制防死锁 jedis.expire(lockKey,(int)(acquireLockTimeoutMs/1000)); Object newValue = loadValueFromDBByKey(key); // 假设这是加载新值的方式 if(newValue != null){ jedis.setex(key,DEFAULT_TTL,newValue.toString()); // 解除锁 jedis.del(lockKey); return true; } } }catch(Exception e){ log.error("Error occurred while refreshing expired key.",e); }finally{ if(jedis!=null){ jedis.close(); } } return false; } ``` #### 方法三:使用布隆过滤器预判是否存在 在查询之前利用布隆过滤器快速判断某条记录是否可能存在于缓存或数据库内。如果布隆过滤器认为存在,则继续访问;反之可以直接告知用户未找到相应信息而无需进一步查找[^3]。 ```java // 这里仅提供思路示意,具体实现依赖于所使用的库 BloomFilter<String> bloomFilter = BloomFilters.create(...); if (!bloomFilter.mightContain(cacheKey)) { System.out.println("The item is definitely not present."); } else { // 可能存在,仍需实际验证 ... } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值