redis缓存击穿解决方案(一)

本文探讨了Redis缓存的适用场景,分析了缓存击穿现象及其解决方案,包括使用互斥锁和队列保证缓存的单线程写入,以及采用双缓存策略应对高并发下缓存失效的问题。

谈到redis缓存,那么什么样的数据适合缓存呢?

分析一个数据是否适合缓存,我们要从访问频率、读写比例、数据一致性等要求去分析. 

那么什么又是缓存击穿呢(服务器宕机不在此讨论范围中,服务高可用)?

在高并发下,多线程同时查询同一个资源,如果缓存中没有这个资源,那么这些线程都会去数据库查找,对数据库造成极大压力,缓存失去存在的意义。

如何解决呢?

一般情况下,加锁访问数据库,伪代码如下

static Lock reenLock = new ReentrantLock();

//最后使用互斥锁的方式来实现.
  public List<String> getData() throws InterruptedException {
        List<String> result = new ArrayList<String>();
        // 从缓存读取数据
        result = getDataFromCache();
        if (result.isEmpty()) {
            if (reenLock.tryLock()) {
                try {
                    System.out.println("我拿到锁,从DB获取数据库后写入缓存");
                    // 从数据库查询数据
                    result = getDataFromDB();
                    // 将查询到的数据写入缓存
                    setDataToCache(result);
                } finally {
                    reenLock.unlock();// 释放锁
                }
            } else {//我没有拿到锁
                result = getDataFromCache();// 先查一下缓存
                if (result.isEmpty()) {
                    System.out.println("我没拿到锁,缓存也没数据,等一下");
                    Thread.sleep(100);// 
                    return getData();// 递归调用重试
                }
            }
        }
        return result;
        }
       或者采用, 队列的方式保证缓存的单线程(进程)写,写数据时加一个标识到队列中,下一个线程过来发现队列有值,不查询数据库,延时再次调用方法。

首先预防高并发访问数据库,防止缓存数据失效:

对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃。采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免对底层存储系统的查询压力。一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短。热点数据定时更新,过期时间分散开。预防后,高并发下缓存失效该如何处理呢?

一,首先

  对于,缓存对象key不变的,value值是变动的,

1,比如说网页首页要展示的一些大广告推广,这些推广是会一直展示的,但数目一段时间内是有限制的,不会一直更新,展示时间也会有条件限制,更新频率半个小时,具体时间或者实时性可根据业务自行决定,某一段时间,此缓存失效,但并发量高,访问数据库,加锁瞬间影响性能,比如秒杀活动,可用双缓存策略,俩服务布双份数据,也就是再加一个缓存服务器,此备用的缓存服务,定时更新若场景为秒杀,可适当多频率同步。主服务缓存失效后,访问备用的缓存服务器,然后更新主服务器缓存,修改主服务器缓存数据。主缓存服务器过期时间短,备用过期时间长点。

 

 

 

<think>嗯,用户想了解如何有效防止缓存击穿缓存穿透和缓存雪崩的问题。我之前看过几个相关的引用资料,需要先回忆下这些概念的区别和对应的解决方案。首先,缓存击穿是指热点数据过期后,大量请求直接打到数据库,可能导致数据库压力过大。缓存穿透是查询不存在的数据,导致绕过缓存频繁访问数据库。缓存雪崩则是大量缓存同时失效,引发数据库请求激增。 根据引用[3],缓存击穿解决方案包括互斥锁和逻辑过期。互斥锁可以用分布式锁,确保只有个线程去重建缓存,其他线程等待。而逻辑过期则是给缓存数据设置个逻辑过期时间,即使物理时间过期,依然使用旧数据,同时异步更新。引用[4]提到缓存穿透可以用布隆过滤器或者缓存空值,布隆过滤器能快速判断数据是否存在,避免无效查询。缓存空值即使数据库没有,也缓存个空值,并设置较短的过期时间,减少数据库压力。 缓存雪崩的解决方案在引用[1]和[2]里有提到,比如设置随机过期时间,避免同时失效;使用多级缓存架构,比如nginx+redis+其他缓存,提高可用性;还有集群部署和限流降级措施。需要把这些方法整理清楚,分门别类地回答用户的问题,并且结合示例代码会更直观。比如用Java实现互斥锁,或者用Redis的SETNX命令。另外,用户可能需要最佳实践,比如综合使用多种策略,根据场景选择合适的方法。还要注意区分这三个问题的不同点,避免混淆解决方案。</think>### 如何有效防止缓存击穿缓存穿透与缓存雪崩 #### 缓存击穿 **问题描述**:热点数据过期时,大量并发请求直接穿透缓存访问数据库,导致数据库压力骤增[^3]。 **解决方案**: 1. **互斥锁(分布式锁)** 使用分布式锁(如Redis的`SETNX`命令),仅允许个线程重建缓存,其他线程等待后重试缓存读取。 ```java public String getData(String key) { String data = redis.get(key); if (data == null) { if (redis.setnx(key + "_lock", "1")) { // 获取锁 data = db.query(key); // 重建缓存 redis.setex(key, 300, data); // 设置缓存 redis.del(key + "_lock"); // 释放锁 } else { Thread.sleep(100); // 等待后重试 return getData(key); } } return data; } ``` 2. **逻辑过期** 数据物理永不过期,但存储逻辑过期时间。当发现过期时,异步更新缓存。 ```java class CacheData { String value; long expireTime; // 逻辑过期时间 } ``` --- #### 二、缓存穿透 **问题描述**:请求不存在的数据,缓存和数据库均无记录,导致频繁无效查询[^4]。 **解决方案**: 1. **布隆过滤器(Bloom Filter)** 在缓存层前添加布隆过滤器,快速判断数据是否存在。若过滤器标记不存在,直接拦截请求。 ```java if (!bloomFilter.mightContain(key)) { return null; // 直接返回,不查缓存和数据库 } ``` 2. **缓存空值** 对查询结果为空的请求,缓存空值并设置短过期时间(如5分钟),避免重复穿透[^4]。 ```java if (dbResult == null) { redis.setex(key, 300, "NULL"); // 缓存空值 } ``` --- #### 三、缓存雪崩 **问题描述**:大量缓存同时失效,导致所有请求涌向数据库[^1]。 **解决方案**: 1. **随机过期时间** 为缓存设置基础过期时间+随机偏移值(如`基础300秒 + 随机0~120秒`),避免同时失效。 ```java int expire = 300 + new Random().nextInt(120); redis.setex(key, expire, data); ``` 2. **多级缓存架构** 采用分层缓存(如Nginx本地缓存 + Redis集群 + 数据库),提升系统容错能力[^2]。 3. **高可用与限流** - 部署Redis集群(主从+哨兵) - 使用熔断机制(如Hystrix)或限流工具(如Sentinel)保护数据库。 --- #### 最佳实践总结 | 问题 | 核心方案 | 适用场景 | |------------|-----------------------------------|---------------------------| | 缓存击穿 | 互斥锁、逻辑过期 | 热点数据高频访问 | | 缓存穿透 | 布隆过滤器、空值缓存 | 恶意请求或无效ID查询 | | 缓存雪崩 | 随机过期时间、多级缓存 | 大规模缓存集中失效 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值