Redis缓存击穿七层防御体系:构建坚不可摧的缓存护甲(PHP版实例)

67 thinkphp 缓存 <?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- // | Copyright (c) 2006~2018 http://thinkphp.cn All rig 阅读详情

 防御体系全景图(思维导图)

实例代码: 

<?php

namespace app\api\controller;

use app\common\controller\Api;
use think\Cache;
use think\Log;
use think\Db;

/**
 * Redis缓存击穿七层防御体系:构建坚不可摧的缓存护甲 接口
 */
class Redisprotect extends Api
{
    // 不需要登录即可访问的方法列表
    protected $noNeedLogin = ['*'];
    // 不需要权限验证即可访问的方法列表
    protected $noNeedRight = ['*'];

    // 第六层:本地缓存
    // 使用静态数组来存储本地缓存,以减少对Redis的访问次数
    protected static $localCache = [];
    // 第七层:请求合并缓存
    // 使用静态数组来合并请求,减少对数据库的查询次数
    protected static $mergeCache = [];

    /**
     * 初始化方法,在类实例化时执行
     * 主要用于构建布隆过滤器,将合法的产品ID标记到Redis位图中
     */
    public function _initialize()
    {
        // 定义布隆过滤器的键名
        $bloomKey = 'bloom:product';
        // 获取Redis的底层操作实例
        $redis = Cache::store('redis')->handler();

        // 模拟从数据库中获取产品ID列表
        $ids = Db::name('product')->column('id');

        // 遍历产品ID列表,为每个ID计算偏移量并设置布隆过滤器中的对应位为1
        foreach ($ids as $id) {
            // 使用crc32哈希算法计算ID的哈希值,并取模得到偏移量
            $offset = crc32($id) % 100000;
            // 在Redis中设置位图的对应位置为1
            $redis->setBit($bloomKey, $offset, 1);
            // 同时将产品信息缓存到本地缓存和请求合并缓存中
            self::$localCache[$id] = self::$mergeCache[$id] = Db::name('product')->find($id);
        }
    }

    /**
     * 第一层护甲:空值缓存(英勇黄铜)
     * 当缓存中没有数据时,即使数据库中也没有数据,也缓存一个空值(如NULL或特殊标记)
     * 以避免反复穿透缓存直接查询数据库
     */
    public function first_layer_of_armor($id)
    {
        // 定义缓存键名,格式为:product:产品ID
        $cacheKey = "product:$id";
        // 尝试从缓存中获取产品信息
        $product = Cache::get($cacheKey);

        // 如果缓存中存在数据(包括空值)
        if ($product !== false) {
            // 如果缓存中存储的是空值标记('NULL'),则返回空值结果
            if ($product === 'NULL') {
                $this->success('缓存命中:空值', null);
            }
            // 如果缓存中存储的是真实产品信息,则直接返回
            $this->success('缓存命中', $product);
        }

        // 如果缓存中没有数据,则尝试从数据库中查找产品信息
        $product = Db::name('product')->find($id);

        // 如果数据库中没有找到对应的产品信息
        if (!$product) {
            // 缓存一个空值标记,防止未来对该ID的无效查询反复穿透缓存
            Cache::set($cacheKey, 'NULL', 300); // 空值缓存5分钟
            $this->success('数据库无数据,已缓存空值');
        }
        // 如果数据库中找到了对应的产品信息,则缓存该信息,并返回给客户端
        Cache::set($cacheKey, $product, 3600);
        $this->success('数据库查询成功并缓存', $product);
    }

    /**
     * 第二层护甲:布隆过滤器(不屈白银)
     * 在缓存访问前增加一层过滤器,拦截绝对不存在的Key请求
     */
    public function second_layer_of_armor($id)
    {
        // 定义布隆过滤器的键名
        $bloomKey = 'bloom:product';
        // 获取底层 Redis 实例
        $redis = Cache::store('redis')->handler();
        // 统一 hash 算法(预存和判断都必须一致)
        // 计算ID的哈希值,并取模得到偏移量
        $offset = crc32($id) % 100000;

        // 使用 getBit 判断布隆过滤器是否命中
        // 如果对应位为0,说明布隆过滤器判断该ID肯定不存在于数据库中
        if (!$redis->getBit($bloomKey, $offset)) {
            // 返回拦截结果
            $this->success('布隆过滤器拦截,非法ID', ['id' => $id]);
        }
        // 如果对应位为1,可能表示ID存在于数据库中,允许继续查询
        $this->success('通过布隆过滤器,允许查询', ['id' => $id]);
    }

    /**
     * 第三层护甲:互斥锁(华贵铂金)
     * 当缓存失效时,仅允许一个线程重建缓存,其他线程等待或轮询
     */
    public function third_layer_of_armor($id)
    {
        // 定义缓存键名,格式为:product:mutex:产品ID
        $cacheKey = "product:mutex:" . $id;
        // 定义互斥锁键名,格式为:lock:product:产品ID
        $lockKey = "lock:product:" . $id;

        // 尝试从缓存中获取产品信息
        $product = Cache::get($cacheKey);
        // 如果缓存中存在数据,则直接返回该数据
        if ($product) {
            $this->success('缓存命中', $product);
        }

        // 如果缓存中没有数据,则尝试获取互斥锁
        // 生成唯一的锁值
        $lockValue = uniqid();

        // 获取底层 Redis 实例
        $redis = Cache::store('redis')->handler();
        // 尝试设置互斥锁,如果设置成功则返回true
        $gotLock = $redis->setex($lockKey, 5, $lockValue);

        // 如果成功获取到互斥锁
        if ($gotLock) {
            try {
                // 从数据库中查找对应的产品信息
                $product = Db::name('product')->find($id);
                // 将查询到的产品信息存入缓存
                Cache::set($cacheKey, $product, 3600);
            } finally {
                // 释放互斥锁,使用Lua脚本保证原子性操作
                $lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                $redis->eval($lua, [$lockKey, $lockValue], 1);
            }
        } else {
            // 如果获取互斥锁失败,则等待1秒后重新尝试
            sleep(1);
            // 递归调用自身重新尝试获取锁
            return $this->third_layer_of_armor($id);
        }

        // 如果重建缓存成功,则返回重建后的数据
        $this->success('互斥锁更新缓存', $product);
    }

    /**
     * 第四层护甲:热点数据永不过期(璀璨钻石)
     * 对极端热点Key,设置逻辑过期时间而非物理过期,异步刷新缓存
     */
    public function fourth_layer_of_armor($id)
    {
        // 定义缓存键名,格式为:hot:product:产品ID
        $cacheKey = "hot:product:" . $id;
        // 尝试从缓存中获取热点数据包装器(包含数据和过期时间)
        $wrapper = Cache::get($cacheKey);

        // 如果缓存中存在包装器,并且当前时间早于包装器中的逻辑过期时间
        if ($wrapper && $wrapper['expire'] > time()) {
            // 返回热点数据
            $this->success('返回热点数据', $wrapper['data']);
        }

        // 如果缓存中没有数据,或者缓存数据已过期,则模拟异步刷新缓存
        // 实际应用中,可以将此操作放入后台任务队列中异步执行
        // 从数据库中查找对应的产品信息
        $product = Db::name('product')->find($id);
        // 创建包含实际数据和逻辑过期时间的数据包装器
        $data = ['data' => $product, 'expire' => time() + 3600];
        // 将数据包装器存入缓存,设置物理过期时间为逻辑过期时间之后的某个时间点
        Cache::set($cacheKey, $data, $data['expire'] + 3600);
        // 返回刷新后的热点数据
        $this->success('逻辑过期已更新热点数据', $product);
    }

    /**
     * 第五层护甲:熔断降级(超超大师)
     * 当数据库压力过大时,主动熔断部分请求,返回兜底数据保护系统
     */
    public function fifth_layer_of_armor($id)
    {
        // 模拟数据库操作的异常概率,假设20%的概率会触发异常
        $failRate = rand(1, 100);
        // 如果异常概率超过设定阈值(80%),则触发熔断机制
        if ($failRate > 80) {
            // 返回兜底数据,保护系统不会因数据库压力过大而崩溃
            $this->success('数据库熔断,返回兜底数据', ['id' => $id, 'name' => '默认商品']);
        }

        // 如果没有触发异常,则正常查询数据库
        $product = Db::name('product')->find($id);
        // 返回正常查询结果
        $this->success('正常返回数据', $product);
    }

    /**
     * 第六层护甲:多级缓存(傲世宗师)
     * 在Redis前增加本地缓存,进一步减少穿透概率
     */
    public function sixth_layer_of_armor($id)
    {
        // 首先检查本地缓存中是否存在对应的产品信息
        if (isset(self::$localCache[$id])) {
            // 如果本地缓存中存在数据,则直接返回
            $this->success('本地缓存命中', self::$localCache[$id]);
        }

        // 如果本地缓存中没有数据,则尝试从Redis缓存中获取
        $redisKey = 'product:level2:' . $id;
        $product = Cache::get($redisKey);

        // 如果Redis缓存中没有数据,则从数据库中查找产品信息,并存入Redis缓存
        if (!$product) {
            $product = Db::name('product')->find($id);
            Cache::set($redisKey, $product, 3600);
        }

        // 将从Redis缓存或数据库中获取的产品信息存入本地缓存
        self::$localCache[$id] = $product;
        // 返回多级缓存中的数据
        $this->success('返回多级缓存数据', $product);
    }

    /**
     * 第七层护甲:请求合并(最强王者)
     * 将短时间内对同一Key的多次请求合并为一次数据库查询,减少数据库压力
     */
    public function seventh_layer_of_armor($id)
    {
        // 首先检查请求合并缓存中是否存在对应的产品信息
        if (isset(self::$mergeCache[$id])) {
            // 如果请求合并缓存中存在数据,则直接返回
            $this->success('请求合并缓存命中', self::$mergeCache[$id]);
        }

        // 如果缓存中没有数据,则从数据库中查询产品信息
        $product = Db::name('product')->find($id);
        // 将查询到的产品信息存入请求合并缓存
        self::$mergeCache[$id] = $product;
        // 返回数据库查询结果
        $this->success('请求合并数据库查询', $product);
    }

}

实战总结

七层防御体系并非需要全量部署,而是根据业务场景灵活组合:

  1. 基础必选:空值缓存 + 互斥锁(90%场景适用)
  2. 热点场景:布隆过滤器 + 永不过期策略(比如明星婚讯、秒杀商品)
  3. 系统加固:熔断降级 + 多级缓存(高并发金融系统)
  4. 极致优化:请求合并(长尾请求优化)
Evolution of Heuristics: Towards Efficient Automatic Algorithm Design Using Large Language Model 启发式算法广泛用于处理复杂的搜索和优化问题。然而,启发式算法的手动设计通常非常耗费人力,需要丰富的工作经验和知识。本文提出了启发式算法的进化 (EoH),这是一种新颖的进化范式,它利用大型语言模型 (LLM) 和进化计算 (EC) 方法进行自动启发式设计 (AHD)。EoH 代表自然语言中的启发式概念,称为思想。然后由 LLM 将它们转换为可执行代码。进化搜索框架中思想和代码的演变使其在生成高性能启发式算法方面非常有效和高效。 阅读详情

相关推荐

WOL唤醒配置(以太网、PHY、MAC)

MAC 就是以太网控制器,属于OSI的数字链路层。phy 属于OSI的物理层(Physical layer),所以叫phy。CPU Mac与phy通信通过MII接口,数据的传输通过MDIO数据线。通过配置phy的相关寄存器实现不同的功能。

qq_58264156的博客 8629

windows环境给redis配置密码

redis安装后目录如下: 最简单的启动方式是直接双击redis-server.exe 如果要设置密码,首先打开配置文件,要注意的是 这两个都是配置文件,记住你改的是哪一个,不放心的可以两个都改。 然后找到#requirepassfoobared,改成requirepass 密码 接着按住shift后右键进入该目录下的命令行,执行redis-server.exe 你改的配...

学亮编程手记 864

python blp模型 估计_随机系数Logit模型及Stata实现

欢迎投稿(荐稿)计量经济圈,计量相关都行邮箱:econometrics666@sina.cn作者:北京交通大学经济管理学院博士生,王琦珀。欢迎你加入计量经济圈社群,一起探讨前沿计量理论和实证方法。一、随机系数Logit模型1.背景实证产业组织(Empirical Industrial Organization,EIO)是目前产业组织理论的前沿,其中又以Ariel Pakes为带头人,其近期工作论文...

weixin_42298927的博客 2641

redis hash 集合使用

//hash使用 $key='vv'; $num = Cache::store('redis')->handler()->hset($key,'2021-01-14',1.6); $a = Cache::store('redis')->handler()->hgetall($key); print_r($a);return 555; //集合使用 $num =...

ahjxhy2010的博客 406

基于轻量级神经网络MobileNet V2的水果识别种类算法研究

本文阐述了一种基于轻量级卷积神经网络MobileNet V2的水果分类与识别算法研究。为了最大限度地促进机械收获,需要一个检测系统,它可以让我们识别不同类别的水果。一个相对未开发的想法是使用深度学习和人工智能算法,不仅可以检测水果的种类,还可以检测水果是否成熟。这可以证明是有利可图的,因为它可以提供作物生长状况以及成熟和未成熟果实的个体数量,以及决定是否是收获时间的有用信息。第一步对数据进行了预处理,数据预处理技术在众多的深度学习计算中起到了重要作用。

qq_43556680的博客 1640

期货套利策略(二)

接上一篇期货套利策略,随着对期货套利策略的深入了解,我发现套利策略不是那么好做的。下面将会讲碰到的一些问题。 上一篇文章中没有考虑滑点的问题,这里我们把滑点问题考虑进去。这里先说一些天勤api的买入点,天琴的买入点是当前K线的收盘价。碰到数据传输问题,还有计算等各方面问题,实际情况是无法在当前K线的收盘价买入的。这里我们把这方面问题考虑进去,利用api.wait_update()调到下一个k线数据...

qq_31611005的博客 1711

TP6操作redis

tp6redis

weixin_36216780的博客 443

ubuntu20.4源码安装最新gcc(gcc V12.2)

Ubuntu20.4自带的gcc为V9.4.0的版本,老版本的gcc存在不支持新版本C语言规范,在配置、编译、安装应用软件或工具时,可能存在不能正确安装的问题。(比如:libpqxx-7.7.4在配置时要求c++17版本的支持,但gcc V9.4.0版本不支持c++17)。因此本文介绍ubuntu20.4的系统下载最新gcc12.2版本源码,并进行本地编译、安装。本节内容包括gcc12.2的必备库安装和gcc12.2安装两部分内容组成。

q544344318的博客 1万+

TP5笔记--持续更新

安装验证码插件 composer require topthink/think-captcha=1.* use think\facade\Request; Request::param('name'); Request::param();全部请求变量 返回数组 Request::param(['name', 'email']); 多个变量 Request::param('a','...

LingMax的博客 653

PCL 建筑物点云立面和平面分割提取【2025最新版】

在建筑物点云中,立面点和平面点的法向量存在明显的差异,根据法向量在Z方向的分量设置相应得阈值即可实现立面点与平面点的分割。

点云侠的博客 1960

starrc: LEF DEF FLOW与full chip flat netlist

SIKP_CELLS默认为"SKIP_CELLS: *"意思是跳过所有ref cell内部的rc提取,仅提取cell之间的互联线的rc,"SKIP_CELLS: *!与MACRO_DEF_FILE搭配使用的还有SKIP_CELLS命令,如果想提取block的寄生参数生成full chip的flat netlist,或者跳过block取执行hier的寄生参数提取都需要用到SKIP_CELLS。此时,macroA和macroB没有被跳过,所以提取其内部的rc,spef中存在两个A和两个B。

m0_61544122的博客 552

ThinkPHP5.1使用redis缓存

redis的使用

北方的猿 2017

【OpenClaw从入门到精通】第34篇:医疗AI的“Agent OS”时代:基于WiseClaw构建医院专属智能体平台(2026实测版)

2026年医疗AI迈入实战落地新阶段,全球首个医疗OpenClaw平台WiseClaw正式发布,香港中文大学(深圳)医院完成业内首个私有化部署。本文基于真实医疗场景需求与公开技术资料,系统拆解医疗智能体平台的建设逻辑:从医疗Agent OS的五大核心特性切入,详解门诊病历质控、用药审查、医院运行管理三大临床场景的智能体搭建流程,提供完整的私有化部署代码、安全配置方案与实操演示。文章兼顾新手入门与进阶需求,严格遵循医疗合规要求,强调“只读不写+人工复核”原则,所有方案均经过虚拟实测验证。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 786

【无标题】REDIS,有序处理

Cache::rm(‘my_sorted_set’); //清除key

qq_35553662的博客 369

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

redis 有序集合使用

//有序集合使用 Cache::store('redis')->handler()->ZADD('hxyg_btc_price',0.17,'2021-01-20'); Cache::store('redis')->handler()->ZADD('hxyg_btc_price',0.15,'2021-01-19'); Cache::store('redis')->handler()->ZADD('hxyg_btc_price',0.14,'2021-01-18'); .

ahjxhy2010的博客 425

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

tp redis批量删除缓冲

$d=Cache::store('redis')->handler()->keys('prefix*'); if(count($d)){ foreach ($d as $v){ Cache::store('redis')->rm($v); } }

apanpan8126的博客 1212

TP5使用复合缓存,拿到句柄对象进行自定义属性

TP5使用复合缓存,拿到句柄对象进行自定义属性 $key=Cache::store('redis')->handler()->set($key,11111111111);

itoof.com专栏 445

ThinkPHP 使用 redis缓存设置

ThinkPHP 使用 redis缓存设置

杰姆小生的博客 2638

PHP用Redis单据锁,防止并发重复写入数据

SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。只在键 key 不存在的情况下, 将键 key 的值设置为 value。若键 key 已经存在, 则 SETNX 命令不做任何动作。用redis 的 setnx 命令实现。

曲帅的博客 584

redis setex和set 的区别

SETEX命令 Redis字符串(String)命令 SETEX key seconds value 将value关联到key, 并将key的生存时间设为seconds(以秒为单位)。 如果key 已经存在,SETEX将重写旧值; 这个命令类似一下两个命令: $redis-&gt;SET('key', 'value'); $redis-&gt;EXPIRE('key','seconds...

赵筱蕊的博客 2万+

Redis删除缓存

Redis删除缓存 环境:Linux下安装redis客户端 1.访问redis根目录 cd /usr/local/redis-2.8.19 2.进入src执行redis-cli 3.执行:dbsize 4.删除所有缓存,执行:flushall 5.退出,执行:exit redis集群指定key值得缓存清理: 1)登录至指定端口的redis服务器 redis-cli -h 127.0.0.1...

lzgoggoo的博客 391

redis缓存的使用 php,thinkphp5.1使用redis缓存

tp5.1出来已经挺久的了,这边记录下tp5.1该怎么使用redis缓存。tp设置配置配置很简单,只需要修改config/cache.php为:return ['type' => 'complex',// 默认'default' => [// 驱动方式'type' => 'File',// 缓存保存目录'path' => '',// 缓存前缀'prefix' =&g...

weixin_39780784的博客 369

TP5 使用redis

1.打开tp5官方手册,在缓存下面找到使用多个缓存类型 粘贴以下代码到tp框架config下面的缓存设置下 // 切换到redis操作 Cache::store('redis')->set('name','value'); //获取redis Cache::store('redis')->get('rename'); //实现redis 消息队列 在 /thinkphp/library/think/cache/driver/Redis.php 文件里面封装 //.

随记 5055

ThinkPHP+Redis实现简单抽奖

tp+redis实现简单抽奖

MrWangisgoodboy的博客 904

TP5中用redis缓存

在config.php配置文件下找到缓存设置,将原来的文件缓存修改为redis缓存,也可以改为多种类型的缓存: // +---------------------------------------------------------------------- // | 缓存设置 // +---------------------------------...

weixin_30443747的博客 342

TP5框架Redis的使用

TP5.0 config类配置redis缓存 'cache' => [ // 使用复合缓存类型 'type' => 'complex', // 默认使用的缓存 'default' => [ // 驱动方式 'type' => 'File', // 缓存保存目录 ...

m0_51200080的博客 2238

ThinkPHP5配置redis缓存

转自:http://www.zzuyxg.top/article/444.htmlthinkphp采用cache类提供缓存功能支持,采用驱动方式,在使用缓存之前需要进行初始化操作。支持的缓存类型包括file、memcache、wincache、sqlite、redis和xcache等,默认情况下是file类型,配置redis缓存可以单一配置redis也可以同时使用多个缓存类型。配置方式分别如下:一...

小璐謌.的博客 2万+
上一篇: Fastadmin固定表头
下一篇: uni-app 开发H5软键盘会顶起底部内容的解决方案
withoutfear
博客等级 码龄13年 541粉丝 79原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值