redis缓存穿透之setnx使用场景

本文探讨了业务增长和高并发下引入Redis缓存的重要性,着重讲解了缓存穿透问题及其影响,如数据库连接超时。提出了利用Redis的setnx命令实现进程锁,确保在缓存失效时只有一个进程读取数据库并更新缓存,从而避免大量请求对数据库造成冲击。通过这种方式,以空间换时间,提高系统效率。

    随着业务的增长,请求并发的增大。很多公司在业务场景中会增加缓存策略,而缓存用的最多的也就是redis了。

    今天我们来说一下缓存穿透,我们缓存一般是有时效性的,一定的生命周期过去之后就会消失,一般的系统不会设置永久性的存储,这时候就会遇到一个问题,要么就是主动刷新缓存,要么就是程序被动刷新缓存。

    事实证明很多程序当中很少主动刷新因为你要去写脚本,定时去刷新数据,这样的话代价比较大。所以较多的就选择了程序被动刷新缓存,就是在缓存失效之后读库在写入缓存。这里就会导致一个问题那就是:当缓存失效的那一刻大量的请求过来就会直接打到数据库上,如果并发量大的话读库操作肯定会有影响,连接超时,连接数过大这样的情况也会随时遇到。

    针对这种情况redis有一个进程锁也就是我们今天说的setnx

    下面直接贴代码:

    $ret = $this->redis->get('k' . $abbr);
    if (empty($ret)) {
    $ret1 = $this->redis->setnx('k'.$abbr,$ret);
        if($ret1){
        $sql = "select * from xxx where a=xx;";
        $query = $this->db->query($sql);
        $ret = $query->result_array();
        $this->redis->set('b' , json_encode($ret) , 6400);
        $this->redis->set('bakk', json_encode($ret) , 10000);
        }    else{
            $ret = $this->redis->get('bakk' . $abbr);
        }

    }

    这段程序的逻辑就是先用一个进程去请求数据如果没有了用setnx锁住这个键只有一个进程去读库操作然后重写两个键值,一个主要的查询键值,一个备份的键值当然缓存时间会长点。这样不管多少请求过来只有一个进程去做读库操作,其他的还是读redis备份数据。用空间来换取时间和效率还是比较划算的。

    这就是针对缓存穿透做的一套操作,下面上一下这个流程图:


<think>我们正在讨论Redis中的缓存穿透缓存击穿问题。根据用户提供的引用材料,我们需要明确这两个概念的区别以及各自的解决方案。注意,用户还提到了缓存雪崩,但问题主要集中在穿透和击穿。 首先,根据引用[1]: - 缓存穿透:个别key没有被命中,导致请求直接作用在数据库上。这种情况是分散的、轻缓的(像一个个点)。 - 缓存击穿:大量请求同时作用在同一个key上,当这个key过期的瞬间,所有请求都打到数据库,导致数据库压力过大(像一条线)。 - 缓存雪崩:大量key集中过期或Redis宕机,导致数据库挂掉(像整个面)。 然后,引用[2][3][4]提供了解决方案。 接下来,我们组织回答: 1. 先分别解释缓存穿透缓存击穿的概念,并强调区别。 2. 分别给出解决方案。 注意:用户要求参考站内引用,因此我们在回答中需要引用这些材料。 回答结构建议: - 缓存穿透 - 概念 - 解决方案(多个方法) - 缓存击穿 - 概念 - 解决方案(多个方法) - 总结区别(可表格对比) 最后,生成相关问题。 注意:回答中需要自然添加引用标识,如[^1][^2]等。同时,确保使用正确的LaTeX格式(虽然这个问题可能数学公式较少,但如果有公式,要按规则书写)。 我们开始构建回答:</think>### Redis 缓存穿透缓存击穿的区别及解决方案 #### **一、核心概念区别** | **问题类型** | **触发条件** | **影响特征** | **形象比喻** | |--------------|----------------------------|-------------------------------------------|----------------------| | **缓存穿透** | 查询**不存在的数据**(如非法ID) | 分散的无效请求穿透Redis直击数据库,压力呈**点状分布** | 雨滴穿透(分散、轻缓)[^1] | | **缓存击穿** | **热点Key突然过期** | 海量请求瞬间压垮数据库,压力呈**线状集中** | 钢针穿刺(集中、猛烈)[^1] | | **缓存雪崩** | 大批Key集中过期或Redis宕机 | 数据库被大面积请求压垮,压力呈**面状覆盖** | 雪山崩塌(广泛、灾难性)[^1] | > 📌 **关键区别**: > - 穿透:**数据本身不存在**(业务逻辑问题) > - 击穿:**热点数据失效**(并发访问问题) --- #### **二、缓存穿透解决方案** 1. **空值缓存** - 将查询为空的Key也存入Redis,设置较短TTL(如30秒)[^4] - 示例代码: ```java if (data == null) { redis.setex("key:null", 30, ""); // 缓存空值 return; } ``` 2. **布隆过滤器** - 在Redis前加布隆过滤器层,拦截非法Key[^4] - 原理: - 使用$k$个哈希函数映射位数组 - 误判率公式:$P \approx (1 - e^{-kn/m})^k$($m$=位数组大小,$n$=元素数) - 实现:RedisBloom模块或Guava库 3. **请求校验** - 接口层校验参数合法性(如ID范围、格式)[^4] --- #### **三、缓存击穿解决方案** 1. **互斥锁(Mutex Lock)** - 当热点Key失效时,仅允许一个线程重建缓存[^4] - 伪代码: ```java public String getData(String key) { String data = redis.get(key); if (data == null) { if (redis.setnx("lock:" + key, "1")) { // 获取锁 data = db.query(key); // 查数据库 redis.setex(key, 300, data); // 重建缓存 redis.del("lock:" + key); // 释放锁 } else { Thread.sleep(50); // 等待重试 return getData(key); // 递归调用 } } return data; } ``` 2. **逻辑过期** - 缓存永不过期,但存储**逻辑过期时间戳** - 异步更新:当检测到数据过期时,触发后台更新[^4] - 数据结构示例: ```json { "value": "真实数据", "expire_ts": 1715000000 // 逻辑过期时间 } ``` 3. **热点数据续期** - 监控高频访问Key(如Redis监控命令`redis-cli --hotkeys`) - 自动延长TTL:在过期前重置为$新TTL = 原TTL \times 1.5$[^2] --- #### **四、防御组合策略** | **层级** | **穿透防护** | **击穿防护** | |----------------|----------------------------|--------------------------| | **接入层** | 参数校验 + WAF防火墙 | 限流(令牌桶算法) | | **缓存层** | 布隆过滤器 + 空值缓存 | 互斥锁 + 逻辑过期 | | **数据层** | 数据库查询限流 | 连接池控制(如HikariCP) | | **监控响应** | 实时告警非法请求IP | 热点Key自动标记[^2] | > 💡 **最佳实践**: > - 穿透场景:布隆过滤器 + 空值缓存(成本低) > - 击穿场景:互斥锁 + 热点监控(保障强一致性)[^4] --- ### **总结对比** | **维度** | **缓存穿透** | **缓存击穿** | |----------------|----------------------------|----------------------------| | **根本原因** | 数据不存在 | 热点Key瞬时失效 | | **请求特征** | 分散、低效 | 集中、高并发 | | **防御重点** | 拦截非法请求 | 控制重建并发度 | | **核心工具** | 布隆过滤器 | 分布式锁 | 通过分层防御策略,可显著降低数据库压力。例如某电商平台采用布隆过滤器+互斥锁后,数据库QPS峰值下降82%[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值