Redis 缓存穿透是什么?如何缓解缓存穿透?

缓存穿透是指在使用缓存技术时,恶意或无效的请求无法从缓存中获取到数据,从而直接落到底层存储系统(如数据库)上,导致频繁地查询底层存储系统,增加系统负载并降低性能。

缓存通常用于存储经常被请求的数据,以提高系统的访问速度。但是,当恶意用户故意发送无效或不存在的请求时,缓存无法命中,导致每次请求都直接访问底层存储系统。如果这种情况持续发生,会对底层存储系统造成严重的压力,并使系统无法承受高负载。

缓解方式

布隆过滤器(Bloom Filter)

布隆过滤器(Bloom Filter)是一种概率型数据结构,用于快速判断某个元素是否属于一个集合,并具有高效的存储和查询性能。它通过使用位数组和多个哈希函数来实现。

布隆过滤器的核心是一个长度为 m 的位数组(bitmap)和 k 个独立的哈希函数。初始时,位数组中的所有位都被置为0。

当要将一个元素插入到布隆过滤器中时,该元素会依次经过 k 个哈希函数的运算,每个哈希函数都会计算出一个位数组的索引位置,将对应位数组位置置为1(即标记)。例如,哈希函数1计算得到索引位置为7,哈希函数2计算得到索引位置为12,那么位数组的第7和第12个位置都会被置为1。

当需要判断一个元素是否属于布隆过滤器时,同样会经过 k 个哈希函数的运算,检查对应索引位置的位数组值是否都为1。如果发现有任意一个位数组位置为0,则可以判定元素不属于布隆过滤器;如果所有位数组位置都为1,则只能说元素可能存在于布隆过滤器中,但并非绝对准确,存在一定的误判率。

布隆过滤器的误判率(false positive)取决于位数组的长度 m 和哈希函数的个数 k,同时也与插入的元素数量和布隆过滤器的设计有关。通过调整位数组长度和哈希函数个数,可以控制误判率的发生概率。

布隆过滤器的主要优点是占用空间小且查询效率高,在处理大规模数据集时,可以有效地降低底层存储系统的访问频率。然而,布隆过滤器也有一些限制,如不支持删除操作、存在一定的误判率和无法获取具体的误判元素等。

布隆过滤器在实际应用中常用于缓存系统、网络爬虫、数据库查询优化以及信息安全等领域,用于过滤掉明显不存在的数据,减少不必要的查询或处理开销,提高系统性能。

安装使用

在 Redis 中,可以使用 RedisBloom 模块来实现布隆过滤器的功能。RedisBloom 是一个开源的 Redis 模块,提供了多种布隆过滤器相关的功能。

要在 Redis 上使用布隆过滤器,需要先安装 RedisBloom 模块。可以通过以下步骤进行安装:

  1. 下载 RedisBloom 源代码:

git clone https://github.com/RedisBloom/RedisBloom.git
  1. 编译 RedisBloom 模块:

cd RedisBloom
make
  1. 将编译生成的 RedisBloom.so 文件复制到 Redis 的模块目录:

cp redisbloom.so /path/to/redis/modules/
  1. 打开 Redis 配置文件 redis.conf,并在 "Modules" 部分添加以下配置:

loadmodule /path/to/redis/modules/redisbloom.so
  1. 重启 Redis 使配置生效。

安装完毕后,就可以使用 RedisBloom 提供的布隆过滤器功能了。以下是一些常用的 RedisBloom 命令:

  • BF.ADD:将元素插入到布隆过滤器中。

    BF.ADD <key> <item>
  • BF.EXISTS:判断元素是否存在于布隆过滤器中。

    BF.EXISTS <key> <item>
  • BF.MADD:批量插入多个元素到布隆过滤器中。

    BF.MADD <key> <item1> [<item2> ... <itemN>]
  • BF.MEXISTS:批量判断多个元素是否存在于布隆过滤器中。

    BF.MEXISTS <key> <item1> [<item2> ... <itemN>]
  • BF.INFO:获取布隆过滤器的基本信息,如误判率、元素数量等。

    BF.INFO <key>

在上述命令中,<key> 代表布隆过滤器的键名,<item> 代表要插入或查询的元素。可以使用不同的键名创建多个独立的布隆过滤器。

需要注意的是,RedisBloom 的布隆过滤器在创建时需要指定预期的元素数量和误判率参数。通过调整这些参数,可以在满足需求的情况下控制内存占用和误判率。

布谷鸟过滤器(Cuckoo Filter)

布谷鸟过滤器(Cuckoo Filter)是一种基于哈希的概率数据结构,用于快速判断一个元素是否在集合中。它是布隆过滤器的一种变体,相比于传统的布隆过滤器,布谷鸟过滤器有较低的空间消耗并提供了更高的查询性能。

布谷鸟过滤器的原理如下:

  1. 布谷鸟过滤器由一个哈希表和一组哈希函数构成。

  2. 对于每个插入的元素,使用哈希函数对其进行多次哈希操作,然后将哈希结果存储在哈希表中。

  3. 当检查一个元素是否存在时,同样使用相同的哈希函数对其进行哈希操作,并查询哈希表中的对应位置。

  4. 如果所有哈希函数得到的位置都包含了此元素,那么插入时这个元素可能在集合中;如果有一个或多个位置为空,那么插入时这个元素一定不在集合中。

布谷鸟过滤器的优点是它不仅可以判断元素是否存在,还可以删除存在的元素。

安装使用

在 Redis 上使用布谷鸟过滤器,可以使用 RedisBloom 模块中的 CF(Cuckoo Filter)命令。以下是一些常用的 RedisBloom CF 命令:

  • CF.ADD:将元素插入到布谷鸟过滤器中。

    CF.ADD <key> <item>
  • CF.EXISTS:判断元素是否存在于布谷鸟过滤器中。

    CF.EXISTS <key> <item>
  • CF.DEL:从布谷鸟过滤器中删除指定元素。

    CF.DEL <key> <item>
  • CF.INSERT:批量插入多个元素到布谷鸟过滤器中。

    CF.INSERT <key> <item1> [<item2> ... <itemN>]
  • CF.EXISTS:批量判断多个元素是否存在于布谷鸟过滤器中。

    CF.EXISTS <key> <item1> [<item2> ... <itemN>]
  • CF.INFO:获取布谷鸟过滤器的基本信息,如过滤器容量、元素数量等。

    CF.INFO <key>

在上述命令中,<key> 代表布谷鸟过滤器的键名,<item> 代表要插入、查询或删除的元素。可以使用不同的键名创建多个独立的布谷鸟过滤器。

需要注意的是,布谷鸟过滤器在创建时需要指定容量参数。通过调整容量参数,可以在满足需求的情况下控制内存占用和查询性能。

与布隆对比

布隆过滤器(Bloom Filter)和布谷鸟过滤器(Cuckoo Filter)都是概率型数据结构,用于判断一个元素是否属于一个集合。它们之间存在一些区别:

  1. 存储结构:

    • 布隆过滤器: 布隆过滤器通常由一个位数组和若干个哈希函数组成,位数组中的每个位表示一个元素的存在与否。

    • 布谷鸟过滤器: 布谷鸟过滤器由一个哈希表和一组哈希函数构成,哈希表中存储了元素的哈希值。

  2. 内存消耗:

    • 布隆过滤器: 布隆过滤器通过位数组来表示元素的存在与否,因此在存储空间上相对紧凑。

    • 布谷鸟过滤器: 布谷鸟过滤器相比于布隆过滤器有较低的空间消耗,但一般会稍微多占用一些内存。

  3. 查找性能:

    • 布隆过滤器: 布隆过滤器具有高效的查询性能,可以在常数时间内判断一个元素是否存在于集合中。但布隆过滤器在查询时存在一定的误判率(可能会误判某个元素存在)。

    • 布谷鸟过滤器: 布谷鸟过滤器在查询性能上通常优于布隆过滤器,可以在常数时间内完成查询操作。而且布谷鸟过滤器对于存在于集合中的元素一定不会产生误判。

  4. 删除操作:

    • 布隆过滤器: 布隆过滤器不支持直接删除元素,因为删除一个元素会影响其他元素的判断结果。如果需要删除元素,通常需要使用其他的技巧,或者重新构建一个新的布隆过滤器。

    • 布谷鸟过滤器: 布谷鸟过滤器支持元素的删除操作,可以直接从过滤器中删除一个存在的元素。

总体而言,布隆过滤器适用于对存储空间有限制且对查询性能要求较高的场景,而布谷鸟过滤器则在一些空间相对宽裕的场景下提供了更好的性能和功能。选择使用哪种过滤器应根据具体的应用需求来决定。

缓存空对象(Cache Null Objects)

当缓存无法命中时,可以在缓存中存储一个特殊的空对象或占位符,表示该请求所对应的数据为空。这样,当再次发生相同的无效请求时,可以直接从缓存中获取到空对象,而不必访问底层存储系统。

实现
  1. 设定一个特殊的值,例如"NULL"或"EMPTY",用于表示空对象。

  2. 在获取对象时,先从Redis中查询对应的键值。

  3. 如果键存在且对应的值不等于特殊值,说明对象存在,直接返回该值。

  4. 如果键不存在或对应的值等于特殊值,说明对象为空,返回空结果。

  5. 当要缓存一个空对象时,将特殊值作为值存储到Redis中对应的键。

Example
require 'predis/autoload.php';
​
// 连接 Redis
$client = new Predis\Client();
​
// 获取对象
function get_object($key) {global $client;$value = $client->get($key);if ($value === null || $value === 'NULL') {return null; // 返回空对象} else {return $value; // 返回对象值}
}
​
// 缓存对象
function cache_object($key, $value) {global $client;if ($value === null) {$client->set($key, 'NULL');} else {$client->set($key, $value);}
}
​
// 示例使用
$object_key = 'my_object';
​
// 获取对象
$cached_object = get_object($object_key);
if ($cached_object === null) {echo "对象不存在或为空\n";
} else {echo "对象值: $cached_object\n";
}
​
// 缓存空对象
cache_object($object_key, null);
​

请求参数校验(Request Parameter Validation)

在应用层面对请求参数进行严格的校验,过滤掉无效或非法的请求。通过验证请求参数的有效性,可以在早期阶段抛弃掉无效请求,从而减轻底层存储系统的负载.

在 Redis 中,可以使用 Lua 脚本来实现请求参数的校验。使用 Lua 脚本可以在 Redis 服务器端执行一系列的操作,并返回结果给客户端,这样可以在单个原子操作中完成参数校验。

Example
lua-- 脚本参数:1-主键,2-请求参数
local key = KEYS[1]
local requestParam = ARGV[1]
​
-- 获取存储在 Redis 中的参数值
local storedParam = redis.call('GET', key)
​
-- 进行参数校验
if requestParam == storedParam then-- 参数校验通过,返回成功结果return "OK"
else-- 参数校验失败,返回错误结果return "ERROR"
end

在 PHP 中,可以使用 Predis 库来执行 Lua 脚本。以下是一个示例代码:

phprequire 'predis/autoload.php';
​
// 连接 Redis
$client = new Predis\Client();
​
// 执行 Lua 脚本进行参数校验
function execute_validation_script($key, $requestParam) {global $client;
​// 定义 Lua 脚本$script = "local key = KEYS[1]local requestParam = ARGV[1]
​local storedParam = redis.call('GET', key)
​if requestParam == storedParam thenreturn 'OK'elsereturn 'ERROR'end";
​// 执行脚本$result = $client->eval($script, 1, $key, $requestParam);
​return $result;
}
​
// 示例使用
$param_key = 'param_key';
$request_param = 'example_value';
​
// 执行参数校验脚本
$result = execute_validation_script($param_key, $request_param);
​
// 处理结果
if ($result === 'OK') {echo "参数校验通过\n";
} else {echo "参数校验失败\n";
}

缓存预热(Cache Pre-warming)

在系统启动或低负载期间,预先加载常用数据到缓存中,提前填充缓存。这样可以避免在高负载时期发生大量的缓存穿透,因为常用数据已经预先缓存,可以直接从缓存中获取,而不必访问底层存储系统。

实现
  1. 确定需要预热的数据:首先确定需要预热的数据集,可以是一些常用的热点数据、经常查询的数据,或者是应用程序启动时必须的数据。

  2. 编写预热脚本:编写一个脚本来加载数据到Redis缓存中。根据数据量的大小和加载过程的复杂程度,可以选择使用编程语言的Redis客户端(如PHP的Predis库)或直接使用Redis的命令行工具(redis-cli)。

  3. 预热数据到Redis缓存:在应用程序启动之前,调用预热脚本来将数据加载到Redis缓存中。可以通过循环遍历数据集并逐个添加到缓存中,或者使用Redis的批量操作命令(如MSET)来更高效地加载数据。

  4. 启动应用程序:在数据加载完成后,启动应用程序。此时应用程序可以直接从Redis缓存中获取数据,而无需从其他数据源(如数据库)中查询数据,提高了响应速度和性能。

require 'predis/autoload.php';
​
// 连接 Redis
$client = new Predis\Client();
​
// 预热数据到Redis缓存
function preload_data() {global $client;
​// 遍历数据集,以键值对的形式添加到Redis缓存中$data = ['key1' => 'value1','key2' => 'value2',// 添加更多的键值对...];
​foreach ($data as $key => $value) {$client->set($key, $value);}
​echo "数据预热完成\n";
}
​
// 执行预热操作
preload_data();
​
// 在应用程序中使用缓存数据
$result = $client->get('key1');
if ($result === null) {echo "缓存中不存在该数据\n";
} else {echo "获取到缓存数据: $result\n";
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/769751.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QB 系统配置模板

查询的时候&#xff0c;直接 在下面添加 一个字段就行&#xff01; public function getDeatil(){$post $this->request->post();if(!isset($post[id])){return out(请传递活动的id);}$builder new Builder($this->getModel());$builder->setFilter([id > …

轨迹预测后处理之非极大值抑制(NMS)

非极大值抑制是图像处理里面的一种算法&#xff08;比如边缘检测会使用到&#xff09; 轨迹预测这里借鉴了其思想&#xff0c;比如说对于某个场景中的某辆车&#xff0c;我们使用模型预测 64 条轨迹或者更多&#xff0c;以很好地捕获多模态性&#xff0c;同时每条轨迹对应一个…

看似简单的SQL,实则就是简单

加班遇到一个SQL问题&#xff0c;本想把别人的SQL改下成SparkSQL&#xff0c;在YARN上运行&#xff0c;然而数据一直对不上。 原SQL ⚠️说明&#xff1a;a.id&#xff0c;b.id没有空的&#xff0c;数据1:1&#xff0c;b.name可能存在空的 select a.id,b.id,b.name from tab…

机器学习方法

机器学习是人工智能&#xff08;AI&#xff09;的一个分支&#xff0c;它使计算机系统能够从数据中学习并改进其性能&#xff0c;而无需进行明确的编程。机器学习的核心是开发算法&#xff0c;这些算法可以从大量数据中识别模式&#xff0c;并用这些模式来做出预测或决策&#…

MySQL数据库的备份

文章目录 MySQL数据库的备份MySQL备份方法完全备份物理备份备份 逻辑热备完全备份逻辑热备恢复恢复库恢复表 增量备份备份增量备份恢复基于位置进行恢复基于时间 MySQL数据库的备份 MySQL备份方法 物理备份&#xff1a; 物理备份涉及直接复制MySQL的数据文件和日志文件。这种…

Javascript中的严格模式 “use strict“

一、为什么使用严格模式&#xff1f; 在普通的 JavaScript 中&#xff0c;写错变量名会创建新的全局变量&#xff0c; 在严格模式中&#xff0c;写错变量名会抛出错误来提醒开发者 二、声明严格模式 通过在脚本或函数的开头添加 “use strict”; 来声明严格模式。"use …

FANUC机器人某个轴编码器损坏时进行单轴零点标定的具体方法

FANUC机器人某个轴编码器损坏时进行单轴零点标定的具体方法 前提: FANUC机器人编码器或其线路有损坏,一般先将机器人移动至零点位置,编码器相关部件更换完毕后,直接进行零点标定即可。但是对于突发的状况,这种方法显然是不行的,比如在生产过程中突然发生碰撞导致编码器相…

暴雨讲堂:AI时代第五代英特尔CPU能做什么?

如果把科技圈比作娱乐圈&#xff0c;那么这两年的顶流一定是AI。2023年&#xff0c;世人见证了ChatGPT在全球范围内的大火&#xff0c;以生成式AI为代表的新一轮人工智能应用问世&#xff0c;改变了人工智能&#xff08;AI&#xff09;技术与应用的发展轨迹&#xff0c;并开始在…

Rancher(v2.6.3)——Rancher部署Nacos(单机版)

Rancher部署Nacos详细说明文档&#xff1a;https://gitee.com/WilliamWangmy/snail-knowledge/blob/master/Rancher/Rancher%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3.md#5rancher%E9%83%A8%E7%BD%B2nacos ps&#xff1a;如果觉得作者写的还行&#xff0c;能够满足您的需求&#x…

Android AIDL编译 程序包 .aidl 不存在

错误: 程序包 aidl 不存在 本文仅针对 AGP 8.0 android gradle plugin 以上 buildFeatures {aidl true } 在模块gradle android { }中增加. 详见: https://developer.android.com/build/releases/past-releases/agp-8-0-0-release-notes?hlzh-cn#default-changes

什么是进程

目录 一. 进程的概念二. 进程的组成三. 进程的特征四. 进程的状态与转换4.1 进程的状态4.2 进程状态的转换4.3 进程的组织方式 五. 进程控制5.1 进程控制5.2 进程创建 六. 进程之间的通信6.1 共享存储6.2 消息传递 \quad 一. 进程的概念 \quad 思考:操作系统是这些进程的管理者…

面试算法-110-课程表

题目 你这个学期必须选修 numCourses 门课程&#xff0c;记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出&#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示如果要学习课程 ai 则 必须 先学习课程 bi 。 …

【分布式websocket】表情怎么做?自制表情包和Unicode表情符号区别?表情编解码?【20期】

前言 表情包是聊天系统中一个比较常见的功能。常见的表情表分为两种是类型。 一种是Unicode表情&#xff0c;另外一种是图片表情。我们这篇文章两种都会涉及。 图片类表情包会涉及到编解码。不会将发送消息的图片的url地址也一并存入数据库中。 文章将会从表情分类。表情编解码…

添加网址到主页

基于localStorage的网址收藏夹-CSDN博客 为了通过安卓菜单添加网址到主页中&#xff0c;调试了几个小时&#xff0c;主要踩了几个坑。 1.localStorage 通过域名隔离&#xff0c;需要加载主页才能读写。 2.WebView 可以不显示&#xff0c;但是 JS 代码要放在 window.onload 中…

oracle19c单机版补丁19.3.0.0升级到19.22.0.0

oracle19c单机版补丁升级到19.22.0.0 一、获取补丁包 查看之前的版本 column product format A30 column version format A15 column version_full format A20 column status format A15 select * from product_component_version;二、备份opatch和数据库文件 su - oracle …

封装-练习

T2、以面向对象的思想&#xff0c;编写自定义类描述IT从业者。设定属性包括&#xff1a;姓名&#xff0c;年龄&#xff0c;技术方向&#xff0c;工作年限&#xff1b;方法包括&#xff1a;工作。 要求&#xff1a; 设置属性的私有访问权限&#xff0c;通过公有的get,set方法实现…

AD域的使用

一&#xff0c;部署共享网络驱动器 1/根目录下创建共享资料库-共享 2/用户-首选项-windows设置-网络驱动器-新建-映射驱动器 3/创建-共享路径-驱动器号(可固定/可随机) 4/更新策略:gpupdate /force 5/客户端查看 首先我们打开服务器此电脑 建立一个文件夹 右击文件夹点击属…

node整理学习(二)

模块化的概念 一、什么是模块化 模块化是指解决一个复杂问题时&#xff0c;自顶向下逐层把系统划分成若干模块的过程&#xff0c;模块是可组合、分解和更换的单元 二、模块化的好处 1、提高了代码的复用性 2、提高了代码的可维护性 3、可以实现按需加载 三、模块化规范 …

Avalonia(11.0.2)+.NET6 打包设置发布包的版本号

Avalonia11.0.2+.NET6 打包设置发布包的版本号 系统版本如何打包设置打包的版本号本文是对上一篇打包文章的补充,后台好多人私信我说打包的版本号如何设置,今天出个补充说明 Avalonia(11.0.2)+.NET6 打包运行到银河麒麟V10桌面系统 系统版本 如何打包 Avalonia(11.0.2)+.NET…

MVC与MVVM:两种前端架构模式对比

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…