学习笔记5:缓存穿透

缓存穿透

缓存穿透是指在缓存系统中,当一个请求的查询结果为空时,这个请求会直接穿透缓存系统,访问后端的数据库。如果这种情况频繁发生,会对数据库造成较大的压力,甚至可能导致数据库崩溃。

在正常情况下,缓存系统会将经常访问的数据存储在内存中,以便快速响应用户的请求。当用户请求某个数据时,系统首先检查缓存中是否存在该数据。如果存在,就直接从缓存中获取数据并返回;如果不存在,则向数据库查询数据,并将查询结果存储到缓存中,然后再返回给用户。

然而,如果某个数据在数据库中也不存在,那么缓存中同样不会有这个数据。这时,每次请求这个数据都会直接访问数据库,而不会经过缓存。这种情况就是缓存穿透。

缓存穿透的问题在于:

  1. 增加数据库压力:大量的空查询会增加数据库的负担,可能导致数据库响应变慢或崩溃。
  2. 降低系统性能:由于缓存系统没有发挥作用,系统的响应速度和吞吐量都会受到影响。

为了解决缓存穿透问题,可以采取以下一些策略:

  • 缓存空值:将查询结果为空的数据也缓存起来,但设置一个较短的过期时间。
  • 使用布隆过滤器:在缓存之前,使用布隆过滤器判断数据是否存在,从而避免对数据库的无效查询。
  • 互斥锁:当缓存中没有数据时,使用互斥锁确保只有一个线程去查询数据库并更新缓存。
  • 限流:对某些频繁查询的请求进行限流,减少对数据库的访问。
  • 数据库优化:优化数据库查询语句,减少查询时间,减轻数据库压力。

通过这些方法,可以有效地减少缓存穿透对系统的影响,提高系统的稳定性和性能。

缓存穿透是指当用户请求的数据在缓存中不存在时,请求会直接穿透到数据库。这不仅增加了数据库的负担,还可能导致数据库崩溃。以下是一些常见的解决方案:

  1. 使用布隆过滤器
    布隆过滤器是一种空间效率很高的数据结构,可以判断一个元素是否在一个集合中。在缓存穿透的情况下,可以使用布隆过滤器存储所有缓存的键,当请求到达时,先在布隆过滤器中判断是否存在,如果不存在,则直接返回,避免对数据库的查询。

  2. 缓存空值
    当查询数据库发现没有数据时,可以将空值也缓存起来,并设置一个较短的过期时间。这样,下次查询时,可以直接从缓存中获取,避免再次查询数据库。

  3. 使用互斥锁
    当缓存中没有数据时,可以使用互斥锁来控制多个线程同时访问数据库。当一个线程开始查询数据库时,其他线程会等待,直到数据库查询完成并更新缓存后,其他线程再继续执行。

  4. 设置过期时间
    对于缓存中的数据,可以设置一个合理的过期时间,这样可以避免缓存中的数据长时间占用内存,同时也能减少缓存穿透的风险。

  5. 增加随机因子
    在缓存空值时,可以增加一个随机因子,使得缓存的过期时间有一定的随机性,这样可以避免大量请求同时到达缓存失效的情况。

  6. 使用分布式缓存
    使用分布式缓存可以提高缓存的可用性和扩展性,同时也能更好地应对高并发的请求。

  7. 数据库查询优化
    优化数据库查询语句,减少查询时间,可以降低缓存穿透对数据库的压力。

  8. 限流
    对用户的请求进行限流,可以减少短时间内大量请求对数据库的冲击。

通过这些方法,可以有效地减少缓存穿透对系统的影响,提高系统的稳定性和性能。

如果系统本身就是依靠缓存来对数据库查询进行分流,那么缓存穿透问题可能不会那么明显,因为缓存的主要作用是减少对数据库的直接访问,提高系统的响应速度和吞吐量。即使在这种情况下,缓存穿透仍然是一个需要关注的问题,因为它可能会导致以下问题:

  1. 数据库压力增加:尽管缓存可以分流,但如果大量请求穿透到数据库,数据库的压力仍然会增加。
  2. 缓存效率降低:缓存的主要目的是减少对数据库的访问,如果大量请求直接访问数据库,缓存的效率就会降低。
  3. 系统稳定性风险:数据库的压力增加可能会影响系统的稳定性,尤其是在高并发场景下。

即使在以缓存分流为主要目的的系统中,也可以采取以下措施来预防或减轻缓存穿透的影响:

  1. 缓存空值:对于数据库查询结果为空的情况,仍然可以将这个空结果缓存起来,并设置一个合理的过期时间。这样,后续的相同请求就可以直接从缓存中获取结果,而不需要再次查询数据库。

  2. 使用布隆过滤器:布隆过滤器可以快速判断一个元素是否在一个集合中,使用它可以在查询数据库之前快速判断数据是否存在,从而避免无效的数据库查询。

  3. 设置合理的缓存策略:根据数据的访问模式和更新频率,设置合理的缓存策略,比如缓存热点数据、设置不同的过期时间等。

  4. 数据库查询优化:优化数据库的查询性能,比如使用索引、优化查询语句等,可以减少数据库处理查询的时间,从而降低缓存穿透的影响。

  5. 限流和降级:在系统压力较大时,通过限流和降级策略来保护系统,避免因为大量请求导致系统崩溃。

  6. 使用分布式缓存:分布式缓存可以提供更好的扩展性和容错性,有助于应对高并发的查询请求。

  7. 监控和报警:对系统进行监控,当检测到缓存穿透的迹象时,及时发出报警,以便采取相应的措施。

通过这些措施,即使在以缓存分流为主要目的的系统中,也可以有效地预防和减轻缓存穿透的影响,确保系统的稳定性和性能。

要使用hiredis来规避缓存穿透,可以通过以下伪代码示例来实现几种常见的策略:

1. 缓存空对象

#include <stdio.h>
#include <stdlib.h>
#include <hiredis/hiredis.h>// 连接到Redis
redisContext* context = redisConnect("127.0.0.1", 6379);
if (context == NULL || context->err) {if (context) {printf("Error: %s\n", context->errstr);redisFree(context);}return;
}// 定义一个函数来处理缓存穿透
void handleCacheMiss(const char* key) {redisReply* reply;char* cacheKey = "cache:shop:";char* cacheValue = NULL;// 拼接完整的缓存键char* fullKey = malloc(strlen(cacheKey) + strlen(key) + 1);strcpy(fullKey, cacheKey);strcat(fullKey, key);// 检查缓存中是否存在数据reply = redisCommand(context, "GET %s", fullKey);if (reply != NULL && reply->type == REDIS_REPLY_STRING) {cacheValue = reply->str;} else {// 数据库查询reply = redisCommand(context, "SELECT * FROM shop WHERE id = %s", key);if (reply != NULL && reply->type == REDIS_REPLY_ARRAY) {// 假设查询到数据,将其转换为字符串并缓存cacheValue = strdup("dummy_value"); // 这里应替换为实际查询结果redisCommand(context, "SET %s %s EX 30", fullKey, cacheValue); // 缓存30秒} else {// 缓存空值redisCommand(context, "SET %s '' EX 30", fullKey); // 缓存空值30秒}freeReplyObject(reply);}freeReplyObject(reply);free(fullKey);
}int main() {const char* shopId = "123";// 处理缓存穿透handleCacheMiss(shopId);redisFree(context);return 0;
}

2. 使用布隆过滤器

布隆过滤器的实现较为复杂,通常需要在应用层实现。以下是一个简化的伪代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <hiredis/hiredis.h>
#include "bloom_filter.h" // 假设已经实现了布隆过滤器// 连接到Redis
redisContext* context = redisConnect("127.0.0.1", 6379);
if (context == NULL || context->err) {if (context) {printf("Error: %s\n", context->errstr);redisFree(context);}return;
}// 初始化布隆过滤器
BloomFilter* filter = bloomFilterCreate(10000, 0.01); // 假设有10000个元素,误判率为1%// 定义一个函数来处理缓存穿透
void handleCacheMiss(const char* key) {redisReply* reply;char* cacheKey = "cache:shop:";char* fullKey = malloc(strlen(cacheKey) + strlen(key) + 1);strcpy(fullKey, cacheKey);strcat(fullKey, key);// 检查布隆过滤器if (bloomFilterCheck(filter, fullKey)) {printf("Key already checked, skip.\n");free(fullKey);return;}// 检查缓存中是否存在数据reply = redisCommand(context, "GET %s", fullKey);if (reply != NULL && reply->type == REDIS_REPLY_STRING) {printf("Cache hit.\n");freeReplyObject(reply);} else {// 数据库查询reply = redisCommand(context, "SELECT * FROM shop WHERE id = %s", key);if (reply != NULL && reply->type == REDIS_REPLY_ARRAY) {printf("Database hit, update cache.\n");redisCommand(context, "SET %s %s EX 30", fullKey, reply->str); // 缓存30秒} else {// 缓存空值redisCommand(context, "SET %s '' EX 30", fullKey); // 缓存空值30秒bloomFilterAdd(filter, fullKey); // 添加到布隆过滤器}freeReplyObject(reply);}free(fullKey);
}int main() {const char* shopId = "123";// 处理缓存穿透handleCacheMiss(shopId);redisFree(context);bloomFilterDestroy(filter); // 销毁布隆过滤器return 0;
}

3. 加互斥锁

#include <stdio.h>
#include <stdlib.h>
#include <hiredis/hiredis.h>
#include <pthread.h>// 连接到Redis
redisContext* context = redisConnect("127.0.0.1", 6379);
if (context == NULL || context->err) {if (context) {printf("Error: %s\n", context->errstr);redisFree(context);}return;
}// 互斥锁
pthread_mutex_t lock;// 定义一个函数来处理缓存穿透
void handleCacheMiss(const char* key) {redisReply* reply;char* cacheKey = "cache:shop:";char* fullKey = malloc(strlen(cacheKey) + strlen(key) + 1);strcpy(fullKey, cacheKey);strcat(fullKey, key);// 获取互斥锁pthread_mutex_lock(&lock);// 再次检查缓存中是否存在数据reply = redisCommand(context, "GET %s", fullKey);if (reply != NULL && reply->type == REDIS_REPLY_STRING) {printf("Cache hit.\n");freeReplyObject(reply);} else {// 数据库查询reply = redisCommand(context, "SELECT * FROM shop WHERE id = %s", key);if (reply != NULL && reply->type == REDIS_REPLY_ARRAY) {printf("Database hit, update cache.\n");redisCommand(context, "SET %s %s EX 30", fullKey, reply->str); // 缓存30秒} else {// 缓存空值redisCommand(context, "SET %s '' EX 30", fullKey); // 缓存空值30秒}freeReplyObject(reply);}// 释放互斥锁pthread_mutex_unlock(&lock);free(fullKey);
}int main() {const char* shopId = "123";// 初始化互斥锁pthread_mutex_init(&lock, NULL);// 处理缓存穿透handleCacheMiss(shopId);redisFree(context);pthread_mutex_destroy(&lock); // 销毁互斥锁return 0;
}

这些伪代码示例展示了如何在C语言中使用hiredis库来实现缓存穿透的规避策略。实际应用中,你可能需要根据具体的业务需求和系统架构进行调整和优化。

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

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

相关文章

对pytorch optimizer中state_dict、state、param_groups的简要理解

先说结论&#xff1a; state_dict()&#xff1a;一个dict&#xff0c;里面有两个key&#xff08;state和param_groups&#xff09;&#xff0c; state这个key对应的value是各个权重对应的优化器状态。具体来说&#xff0c;一个model有很多权重&#xff0c;model.parameters()会…

MyBatis相关问题汇总

sql预编译原理 https://www.cnblogs.com/Createsequence/p/16963891.html MyBatis一级缓存&二级缓存 mybatis的缓存机制&#xff08;一级缓存二级缓存和刷新缓存&#xff09;和mybatis整合ehcache_mybatis缓存机制-CSDN博客

每日一题~961div2A+B+C(阅读题,思维,数学log)

A 题意&#xff1a;给你 n*n 的表格和k 个筹码。每个格子上至多放一个 问至少占据多少对角线。 显然&#xff0c;要先 格数的多的格子去放。 n n-1 n-2 …1 只有n 的是一个&#xff08;主对角线&#xff09;&#xff0c;其他的是两个。 #include <bits/stdc.h> using na…

基于Java和MySQL的数据库优化技术

基于Java和MySQL的数据库优化技术 大家好&#xff0c;我是微赚淘客系统3.0的小编&#xff0c;是个冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将探讨如何基于Java和MySQL进行数据库优化&#xff0c;提升系统的性能和稳定性。我们将从查询优化、索引使用…

管理和迁移Conda环境两种方法:conda env export 和 Conda-Pack

在管理和迁移Conda环境时&#xff0c;通常有两种常用的方法&#xff1a;conda env export 和 Conda-Pack。这两种方法各有优缺点&#xff0c;根据具体需求可以选择合适的方法。 方法一&#xff1a;Conda env export conda env export 是Conda自带的命令&#xff0c;用于导出当…

基于微信小程序图书馆座位预约管理系统设计与实现

1.1选题动因 当前的网络技术&#xff0c;软件技术等都具备成熟的理论基础&#xff0c;市场上也出现各种技术开发的软件&#xff0c;这些软件都被用于各个领域&#xff0c;包括生活和工作的领域。随着电脑和笔记本的广泛运用&#xff0c;以及各种计算机硬件的完善和升级&#x…

JS 事件循环(Event Loop)机制

事件循环机制的作用 事件循环机制是 JS 的一种执行机制&#xff0c;一种可以实现异步编程的机制。 因为 JS 是单线程的&#xff0c;单线程意味着所有任务需要排队执行。但是有一些 API&#xff08;比如&#xff1a;定时器和 Ajax 等&#xff09;是需要等待一定的时间才能得到…

【Python】一文向您详细介绍 K-means 算法

【Python】一文向您详细介绍 K-means 算法 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&#xff…

Visual Studio 2022新建 cmake 工程测试 tensorRT 自带样例 sampleOnnxMNIST

1. 新建 cmake 工程 vs2022_cmake_sampleOnnxMNIST_test( 如何新建 cmake 工程&#xff0c;请参考博客&#xff1a;Visual Studio 2022新建 cmake 工程测试 opencv helloworld ) 2. 删除默认生成的 vs2022_cmake_sampleOnnxMNIST_test.h 头文件 3. 修改默认生成的 vs2022_cma…

【 C语言 】 C语言设计模式

一 、C语言和设计模式&#xff08;继承、封装、多态&#xff09; C有三个最重要的特点&#xff0c;即继承、封装、多态。我发现其实C语言也是可以面向对象的&#xff0c;也是可以应用设计模式的&#xff0c;关键就在于如何实现面向对象语言的三个重要属性。 &#xff08;1&…

BSV区块链在人工智能时代的数字化转型中的角色

​​发表时间&#xff1a;2024年6月13日 企业数字化转型已有约30年的历史&#xff0c;而人工智能&#xff08;以下简称AI&#xff09;将这种转型提升到了一个全新的高度。这并不难理解&#xff0c;因为AI终于使企业能够发挥其潜力&#xff0c;实现更宏大的目标。然而&#xff0…

MySQL中实现动态表单中JSON元素精准匹配的方法

目录 前言 一、动态表单技术 1、包含的主要信息 2、元素属性设置 3、表单内容 二、表单数据存储和查询 1、数据存储 2、数据的查询 3、在5.7版本中进行JSON检索 4、8.0后的优化查询 三、总结 前言 在很多有工作流设置的地方、比如需要在不同的流程中&#xff0c;需要…

什么是跨域问题及其解决方案

什么是跨域问题及其解决方案 在现代Web开发中&#xff0c;跨域问题是一个常见的挑战。了解什么是跨域问题以及如何解决它&#xff0c;对于开发者来说至关重要。在这篇博客中&#xff0c;我们将详细介绍什么是跨域问题&#xff0c;并探讨几种常用的解决方案。 什么是跨域问题&…

Docker 搭建GitLab

# 拉取镜像 docker pull gitlab/gitlab-ce # GitLab 需要持久存储来保存数据&#xff0c;如仓库数据、配置 mkdir -p /opt/gitlab/config /opt/gitlab/logs /opt/gitlab/data # 使用 docker run 命令来启动 GitLab 容器 docker run -itd \--hostname 192.168.111.128 \--p…

服务器数据恢复—V7000存储硬盘故障脱机的数据恢复案例

服务器存储数据恢复环境&#xff1a; 某品牌P740小型机AIXSybaseV7000磁盘阵列柜&#xff0c;磁盘阵列柜中有12块SAS机械硬盘&#xff08;其中包括一块热备盘&#xff09;。 服务器存储故障&#xff1a; 磁盘阵列柜中有一块磁盘出现故障&#xff0c;运维人员用新硬盘替换掉故障…

网络安全等级保护解决方案的主打产品

网络安全等级保护解决方案的主打产品&#xff1a; HiSec Insight安全态势感知系统、 FireHunter6000沙箱、 SecoManager安全控制器、 HiSecEngine USG系列防火墙和HiSecEngine AntiDDoS防御系统。 华为HiSec Insight安全态势感知系统是基于商用大数据平台FusionInsight的A…

【LeetCode】201. 数字范围按位与

1. 题目 2. 分析 这题挺难想的&#xff0c;我到现在还没想明白&#xff0c;为啥只用左区间和右区间就能找到目标值了&#xff0c;而不用挨个做与操作&#xff1f; 3. 代码 class Solution:def rangeBitwiseAnd(self, left: int, right: int) -> int:left_bin bin(left).…

lightningcss介绍及使用

lightningcss介绍及使用 一款使用 rust 编写的 css 解析器&#xff0c;转换器、及压缩器。 特性 特别快&#xff1a;可以在毫秒级别解析、压缩大量的 css 文件&#xff0c;而且比其他工具的打包结果更小给值添加类型&#xff1a;许多其他css解析器会将值解析成一个无类型的 …

k8s集群可视化工具安装(dashboard)

可视化安装 2.1、下载相关的yaml文件 wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml Vim recommended.yaml 2.2、部署 kubectl apply -f recommended.yaml 查看那kubernetes-dashboard命令空间下的资源 kubectl get …

ZLMRTCClient配置说明与用法(含示例)

webRTC播放视频 后面在项目中会用到通过推拉播放视频流的技术&#xff0c;所以最近预研了一下webRTC 首先需要引入封装好的webRTC客户端的js文件ZLMRTCClient.js 下面是地址需要的自行下载 http://my.zsyou.top/2024/ZLMRTCClient.js 配置说明 new ZLMRTCClient.Endpoint…