缓存击穿,商详页进不去了!!!

news2024/11/28 15:40:11

故事

对于小猫来讲,最近的一段日子是不好过的,纵使听着再有节拍的音乐,也换不起他对生活的热情。由于上一次“幂等事件”躺枪,他已经有几天没有休息好了。他感觉人生到了低谷。

当接手这个商城项目之后,他感觉他一直没有好过。他的内心彷徨,在工位上边写着事故报告,边嘀咕着“今年到底是犯了啥冲...为什么...”

然而屋漏又遭连夜雨,船破偏遇当头风,好像坏事儿又找上了他。

坐他旁边的哥们在一旁抱怨,“啥情况,我就想给公司助助力,买点咱自家公司的产品,咋商详页咋点来点去进不了,你看看你的呢?”。

“你自己手机不行了吧,你瞧你那老破烂也该换了,iphone15 pmax搞起...” 旁边的老六开涮道。

然而却触动了小猫的神经,他赶紧打开app,发现自己的也进不去了。他赶紧打开Grafana,脸色苍白,口干舌燥....数据库连接全部打满。

不到一会组长的电话也收到了客服反馈的客诉,组长向小猫投来质疑的目光。

小猫无辜而又无奈:“我真的没有动过代码......”。

经过一轮彻彻底底地摸排,事情的原因也终于水落石出。

大概如下图这么回事儿:

图片

流程

上次A公司对接商城服务之后开始在他们商城平台推拼团售卖活动,活动中的几个商品的缓存被删除之后没有恢复,原因是因为第三方对接的API商品大量推送,队列有堆积,导致redis缓存并没有及时更新。大批量的用户在进行购买活动商品的时候,请求全部打到了DB上。

反正也不晓得是哪个挨千刀的设计的技术方案,缓存到redis中的时候用的居然是异步消息队列。商品发布量小,访问量小的时候可能没有什么问题。

但是偏偏各种巧合发生在了一起就产生了这样一个事故,这可能就是所谓的墨菲定律吧。

小猫已经走到了绝望的边缘......(下次就不说小猫踩坑了,让他缓几天,哈哈)

聊聊缓存击穿

在此我们对小猫表示同情,但是这是一个很好的例子,还是和大家一起聊聊缓存击穿雪崩的话题。

咱们就从以下几个方面聊聊缓存雪崩击穿的问题。

图片

概览图

在上图中可以看见,我们会对布隆过滤器做一个重点的介绍,因为这个也是比较常用的方式缓存击穿的方式。

什么是缓存击穿?是什么原因导致的?

从上面小猫的案例中,其实就已经很明了了,所谓缓存击穿就是原本由于缓存组件抗住的流量结果全部打到了数据库层,给数据库带来了巨大的压力,甚至严重的情况下直接把数据库干跨。导致缓存失效的原因也是很显然易见的,由于缓存在一个无法预期的一个场景下缓存失效了。

在小猫的案例中可以看到是热卖的商品在redis中Key值全部同时失效导致的。当然这是一种常见的技术方案有问题导致的。那么还有一种导致缓存失效的原因就是缓存中间件直接宕机。这种情况是运维层面需要解决的,可能要求对缓存中间件做好高可用,如何做高可用,我们在此不做深究。

下面是咱们的缓存用法,大家可以看一下下面的流程。

图片

缓存流程

从上述的流程图中我们可以看到,当有请求过来的时候,首先会尝试从缓存中去读取,当缓存中没有读到的时候,这个时候咱们就会回源到数据库再次查找,当查到相关商品数据之后返回给客户端,之后并将该数据继续缓存到数据库中。

接下来咱们来看一下发生雪崩之后的解决方案。

无效key值处理

这种情况其实很简单了,既然由于没有在缓存数据库中找到数据,那么咱们也不应该直接将redis中那些Key值直接删除,这样找不到key的话,肯定还是会打到数据库,所以我们只要避免请求去查数据库就可以了。所以我们就把无效的Key也保存到redis中即可。这种值的话,我们都保存成“null”。这样的话redis中的key一定就是全量的了,客户端查询数据的时候顶多也就是查不到。

这种方案可能会影响到用户体验,我们对这个方案其实也可以做一下改造,就是利用异步定时任务重新检测缓存中为null的数据,异步去刷新数据到redis中。但是还是没有从根本解决客户体验的问题,只是尽量降低客户的不满意度。大概流程如下。

图片

无效key+定时任务

这种方案的缺点就是存在用户体验问题。

另外的这种方案还有一个缺点,大家思考一下,我们将失效的key以null值的形式缓存到redis中,但是如果有个恶意攻击的行为,专门挑一些随机的key去攻击我们的接口的时候,请求是不是还是最终会打到数据库,所以这种方案不是万无一失的也不是最好的,提到的布隆过滤器则不会存在这样的问题,后面我们再看。

互斥锁方案

我们看到导致数据库雪崩是由于请求太大穿透到数据库,那么我们可以在访问数据库的时候动动脑筋。我们可以在访问数据这个环节中加锁,虽然影响性能,但是对于系统来说是安全的。这种方案和无效key方案进行组合之后其实可以用来作为兜底方案,搭配使用其实效果也不错的。我们来看下图。

图片

缓存回源加锁

这种情况就是在上面的标准缓存流程中回溯数据库的地方利用redis的特性 setNx加了一把互斥锁,这样的话,咱们能够尽量保证少量的请求打到数据库中。

大白话可以这么理解,张三李四去同时去访问同一个商品的时候,他们两只有一个能成功,成功拒绝了并发时候针对同一个热门商品的访问。

热点数据限流访问

关于这个,我想其实可以不做太多展开,思路很简单,既然是由于请求量太大,导致不走缓存走到了DB,从而将DB打垮,那么咱们就限流量呗。请求量少了,那么自然不会打垮数据库了。关于限流的一些措施以及算法,所以在此也不做赘述。大家有兴趣的话可以访问:

布隆过滤器

如果用上布隆过滤器的话,那么我们方案如下调整。

图片

系统在初始化的时候将key初始化到布隆过滤器中。当查询的时候,把布隆过滤器放到查询redis缓存之前,如果发现存在Key在布隆过滤器中,那么就继续后续的流程,如果不存在则根本就连摸缓存的机会都没有,更别提数据库了,这样就成功避免了恶意采用无用的key值进行攻击。

什么是布隆过滤器

关于布隆过滤器,在此也展开说一下,因为觉得这个可能是这些防雪崩方案中最好的一种方案。那么咱们来看一下什么是布隆过滤器。

这种过滤器是一个叫做Bloom的哥们于1970年提出的,咱们可以把它看做由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。位数组即(bit数组),bit是计算机中最小的单位,也就是我们常说的计算机中的0和1,这种就是用一个位来表示的。

Bit数组大概长的就是如下这样的。

图片

bit数组

位数组中的每个元素都只占用 1 bit , 并且每个元素只能是0 或 1 。这样申请一个 100w 个元素的位数组只占用 1000000Bit / 8 = 125000 Byte = 125000 / 10214 kb ≈ 122kb 的空间。所以占用空间极小。

布隆过滤器原理

其基本原理是利用多个哈希函数,将一个元素映射成多个位,然后将这些位设置为 1。当查询一个元素时,如果这些位都被设置为 1,则认为元素可能存在于集合中,否则肯定不存在。所以,布隆过滤器可以准确的判断一个元素是否一定不存在,但是因为哈希冲突的存在,所以他没办法判断一个元素一定存在。只能判断可能存在。

如下图:

图片

  • 添加元素的流程。

    1. 针对相同的key通过不同的hash计算,算出不同的值。

    2. 分别更新数组中的数据值为1。

  • 查询元素的流程。例如上图,当查询一个不存在的Key3的时候,调用hash1以及hash2函数,恰好命中了之前key1和key2的hash值,此处我们就有可能误判觉得Key3值也是存在的。这样的话也会打到后续流程中去做查询的业务动作。

手撸一个简单的java布隆过滤器

丐版的布隆过滤器的实现方式其实还是比较容易的。如下源码:

/**
 * @author 
 * @date 2024/1/18 23:30
 */
public class BloomFilter {
    /**
     * 初始化大小
     */
    private static final int DEFAULT_SIZE = 2 << 24;
    /**
     * 位数组。数组中到元素只能是 0 和 1
     */
    private BitSet bits = new BitSet(DEFAULT_SIZE);
    /**
     * 计算hash值
     * @param key
     * @return
     */
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : Math.abs((h = key.hashCode()) ^ (h >>> 16));
    }
    /**
     * 添加元素到位数组
     */
    public void add(Object value) {
        bits.set(hash(value), true);
    }
    /**
     * 判断指定元素是否存在于位数组
     */
    public boolean contains(Object value) {
        return bits.get(hash(value));
    }
}

写个main函数测试一下:

/**
 * @author 
 * @date 2024/1/18 23:33
 */
public class Test {
    public static void main(String[] args) {
        String value1 = "https://blog.ktdaddy.com/";
        String value2 = "kdaddy2";
        BloomFilter filter = new BloomFilter();
        System.out.println(filter.contains(value1));
        System.out.println(filter.contains(value2));
        filter.add(value1);
        filter.add(value2);
        System.out.println(filter.contains(value1));
        System.out.println(filter.contains(value2));
    }
}

结果输出也很简单:

  false
  false
  true
  true
其他第三方布隆过滤器

当然Java中还可以使用第三方库来实现布隆过滤器,常见的有Google Guava库和Apache Commons库以及Redis。关于这两种过滤器的用法,在此就不做赘述了,篇幅过长,大家可能都会丧失读下去的欲望了,所以就到此打住,感兴趣的小伙伴可以自行去找一下相关的资料,然后写个demo玩玩。

写在最后

上述基于小猫的痛苦之上给大家分享了缓存击穿的一系列的解决方案,如果大家还有补充的话,也欢迎大家能够在评论区留言。有个问题想问一下大家,当你新接手一个你不熟悉的项目的时候,你做的第一件事情是什么?

先说一下自己吧,我一般会将现有的业务模型梳理一下,即相关的表结构,然后将核心的流程画一画,继而通过一些列新的迭代慢慢熟悉整个系统,当然在此期间其实也会遇到这样的各种各样的坑,无论是技术方案的坑还是说代码的坑。其实还是比较喜欢“早发现早治疗”这种方式,发现问题,尽早解决掉,个人还是比较抵触现在网上比较流行的说法“防御式编程”。因为如果有问题等到真的爆发的时候,有可能就是灾难性的。

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

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

相关文章

【ChatGPT】文本向量化与余弦相似度:揭开文本处理的神秘面纱(5)

1、引言 在这个数字化的时代&#xff0c;我们每天都会面对大量的文本信息&#xff0c;从社交媒体到新闻报道&#xff0c;文本无处不在。但是&#xff0c;计算机要如何理解和处理这些文字呢&#xff1f;本文将为大家揭开其中的一些奥秘&#xff0c;详细解释文本向量化的概念&am…

构建中国人自己的私人GPT—支持中文

上一篇已经讲解了如何构建自己的私人GPT&#xff0c;这一篇主要讲如何让GPT支持中文。 privateGPT 本地部署目前只支持基于llama.cpp 的 gguf格式模型&#xff0c;GGUF 是 llama.cpp 团队于 2023 年 8 月 21 日推出的一种新格式。它是 GGML 的替代品&#xff0c;llama.cpp 不再…

【Java程序设计】【C00209】基于SSM个人求职管理系统(论文+PPT)

基于SSM个人求职管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这个一个基于SSM的个人求职管理系统&#xff0c;本系统共分为三种权限&#xff1a;管理员、普通管理员、用户 管理员&#xff1a;首页、个人中心、用户管理、管理…

免费使用支持离线部署使用的 txt2video 文本生成视频大模型(Text-to-Video-Synthesis Model)

免费使用支持离线部署使用的 txt2video 文本生成视频大模型(Text-to-Video-Synthesis Model)。 文本生成视频大模型(Text-to-Video-Synthesis Model)是一种基于深度学习技术的人工智能模型&#xff0c;它可以将自然语言文本描述转换为相应的视频。即通过输入文本描述&#xff…

【亿级数据专题】「分布式消息引擎」 盘点本年度我们探索服务的HA高可用解决方案

盘点本年度我们探索服务的HA高可用解决方案 前言介绍HA高可用高可用性评估可用性是平均故障间隔时间 HA技术架构的特性分析Master/Slave架构主从复制模式主从复制的特性分析 高可用案例RocketMQ的主从架构责任划分同步机制动态化RocketMQ高可用架构中有限状态机的转换 总结Mast…

十五届健康中国大会在京举行,1892本草茶助力健康中国2030

2024 年1 月26 日,人民日报健康客户端主办的第十五届健康中国论坛在北京人民日报健康客户端演播厅举行。 健康中国论坛作为我国医药卫生界以“健康中国”为主题的论坛中举办时间最早、影响最大、质量最高、权威性最强的会议之一,今年走过了第十五个年头,已成为各级党和政府、医…

【芯片设计- RTL 数字逻辑设计入门 番外篇 8.1 -- memory repair 详细介绍】

文章目录 memory repair 详细介绍Memory Repair 方法Memory Repair 过程举例memory repair 详细介绍 SoC (System on Chip) 的 Memory Repair 是一种技术,用于检测和修复内存中的损坏单元。由于SoC内部集成了大量的逻辑和存储单元,包括RAM(随机访问存储器)、ROM(只读存储…

双目相机立体匹配基础

双目匹配就是用左相机和右相机去拍摄同一个点&#xff0c;目的是找到三维世界的同一个点&#xff0c;也就是在左相机和右相机中的成像点之间的像素差&#xff08;视差&#xff09;&#xff0c;根据视差去求解深度&#xff0c;那么找到左相机点到右相机的同一个对应点这个过程就…

【五】【C++】类与对象(三)

const只读 在 C 中&#xff0c;const 关键字用于声明一个变量为常量&#xff0c;意味着一旦被初始化之后&#xff0c;它的值就不能被改变。 声明常量&#xff1a; 使用 const 关键字可以声明变量为常量。这意味着这个变量的值不能被修改。 const int MAX_SIZE 100; 指针与…

启动盘重装ubuntu22系统

win+R msinfo32查看 插入制作好的u盘电脑开机 进入BIOS界面的方法有多种,以下是一些常见的方法: 进入BIOS界面的最常见按键有: Del键:大多数台式机通过在启动时按下Del键来进入BIOS。Esc键:在AMI BIOS和某些品牌电脑中,进入BIOS系统需要按“Esc”键,一般在开机画面…

简单实践 java spring cloud 负载均衡

1 概要 1.1 实现一个最简单的微服务。远程调用负载均衡&#xff0c;基本上完成了最核心的微服务框架。 远程调用&#xff1a;RestTemplate 注册中心&#xff1a;eureka 负载均衡&#xff1a;Ribbon 1.2 要点 1.2.1 依赖 1.2.1.1 主框架依赖 spring boot 依赖 <depe…

SpringMVC处理ajax请求之@ResponseBody注解,将后端数据响应到浏览器

上一篇文章讲到SpringMVC处理ajax请求用到的RequestBody注解SpringMVC处理ajax请求&#xff08;RequestBody注解&#xff09;&#xff0c;ajax向后端传递的数据格式详解-CSDN博客&#xff0c;这个注解帮我们解决了如何将客户端的数据通过json数据传递到服务器&#xff0c;简单说…

基于ARM的餐厅点餐系统的设计与实现

基于ARM的餐厅点餐系统的设计与实现 系统简介 本设计主要将 STM32F103ZET6 芯片作为无线订购系统主要控制芯片&#xff0c;分为顾客终端和厨师终端。顾客通过 LCD 显示屏浏览菜单并点击触摸屏选择自己所需菜单&#xff0c;并经过有线连接到 PC 端上位机&#xff0c;将订餐信息…

人工智能基础-Numpy矩阵运算-聚合操作

加、减、乘、除、整除 幂、取余、倒数、绝对值 三角函数 e的x次方、3的x次方、logx、log2为底、log10为底 矩阵运算 加、减、乘&#xff08;对应数相乘&#xff09;、矩阵相乘运算、转至 向量和矩阵的运算 加法 对应相加 改变维度后相加 乘法 矩阵的逆 聚合操作 …

单元测试实践

一、写在开始写单元测试前 1.1 背景 我们开发都知道单元测试的重要性&#xff0c;而且每个开发都有要写单元测试的意识单元测试和代码编写结构息息相关&#xff0c;业界常用专业名词TDD&#xff08;测试驱动开发&#xff09;&#xff0c;言外之意我们开始编写代码的时候就已经…

如何使用Cloudreve搭建私有云盘并发布公网访问无需购买域名服务器

文章目录 1、前言2、本地网站搭建2.1 环境使用2.2 支持组件选择2.3 网页安装2.4 测试和使用2.5 问题解决 3、本地网页发布3.1 cpolar云端设置3.2 cpolar本地设置 4、公网访问测试5、结语 1、前言 自云存储概念兴起已经有段时间了&#xff0c;各互联网大厂也纷纷加入战局&#…

自学Java的第54、55、56、57天

多线程 创建方式一 写法 注意&#xff1a; 创建方法二 写法 写法 简化&#xff1a; 创建方法三 写法 Thread的常用方法 写法 线程安全 用程序模拟 解决方法&#xff1a;线程同步 方法一 同步代码块 写法 方法二 同步方法 写法 方法三 Lock锁 写法 线程通信&#xff08;了解&…

壹[1],Xamarin开发环境配置

1&#xff0c;环境 VS2022 注&#xff1a; 1&#xff0c;本来计划使用AndroidStudio&#xff0c;但是也是一堆莫名的配置让人搞得很神伤&#xff0c;还是回归C#。 2&#xff0c;MAUI操作类似&#xff0c;但是很多错误解来解去&#xff0c;且调试起来很卡。 3&#xff0c;最…

企业计算机服务器中了mkp勒索病毒怎么办,mkp勒索病毒解密流程

网络是一把双刃剑&#xff0c;随着网络技术的不断发展与应用&#xff0c;企业的生产效率大大提升&#xff0c;企业的数据安全关乎着企业的发展&#xff0c;保护好企业的数据直观重要&#xff0c;近期&#xff0c;云天数据恢复中心接到很多企业的求助&#xff0c;企业的计算机服…

Mistral-7B本地运行【Ollama】

Mistral AI 目前提供两种类型的大型语言模型访问方式&#xff1a; 提供按使用量付费访问最新模型的 API&#xff0c;开源模型可在 Apache 2.0 许可证下使用&#xff0c;可在 Hugging Face 上或直接从文档中获取。 在本指南中&#xff0c;我们概述了 Mistral 7B LLM 以及如何提…