一次缓存失效引发的惨案!

news2024/11/15 20:11:06

分享是最有效的学习方式。

故事

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

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

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

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

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

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

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

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

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

大概如下图这么回事儿:

在这里插入图片描述

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

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

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

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

聊聊缓存击穿

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

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

在这里插入图片描述

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

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

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

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

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

在这里插入图片描述

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

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

无效key值处理

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

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

在这里插入图片描述

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

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

互斥锁方案

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

在这里插入图片描述

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

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

热点数据限流访问

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

限流方案

这里还有相关的demo源码。有兴趣的可以自取。

布隆过滤器

如果用上布隆过滤器的话,那么我们方案如下调整。
在这里插入图片描述

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

什么是布隆过滤器

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

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

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/1397387.html

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

相关文章

Linux 为何不把图形用户界面写入内核?

Linux 为何不把图形用户界面写入内核&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「Linux的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#…

禅道:从安装到使用,一篇文章带你全面了解

博客前言&#xff1a; 在这个充满竞争和快节奏的世界里&#xff0c;项目管理已经成为了许多行业的关键环节。禅道作为一种功能强大、易用的项目管理工具&#xff0c;正在被越来越多的企业和团队所采用。它不仅能帮助我们高效地管理项目&#xff0c;还能提升团队协作和沟通的效…

DC-1靶机刷题记录

靶机下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1GX7qOamdNx01622EYUBSow?pwd9nyo 提取码&#xff1a;9nyo 参考答案&#xff1a; https://c3ting.com/archives/kai-qi-vulnhnbshua-tiDC-1.pdf【【基础向】超详解vulnhub靶场DC-1】 https://www.bilibi…

【数据结构二】链表和LinkedList详解

目录 链表和LinkedList 1.链表的实现 2.LinkedList的使用 3.ArrayList和LinkedList的区别 4.链表OJ题训练 链表和LinkedList 当 在 ArrayList 任意位置插入或者删除元素时&#xff0c;就需要将后序元素整体往前或者往后 搬移&#xff0c;时间复杂度为 O(n) &#xff0c;效率…

ChatGLM-6B部署和微调实例

文章目录 前言一、ChatGLM-6B安装1.1 下载1.2 环境安装 二、ChatGLM-6B推理三、P-tuning 微调3.1微调数据集3.2微调训练3.3微调评估3.4 调用新的模型进行推理 总结 前言 ChatGLM-6B ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型&#xff0c;基于 General Language Mo…

unity-shader笔记OLD

shader shader在面板中的位置相关代码代码切换shader shader在面板中的位置 选中物体属性面板中 相关代码 代码切换shader 挂载到怪物上的shader名字统一叫body&#xff0c;然后获取上面的SkinnedMeshRender SkinnedMeshRender smr&#xff1b; //恢复到原来的shader …

JavaScript DOM可以做什么?

1、通过id获取标签元素 DOM是文档对象模型&#xff0c;它提供了一些属性和方法来方便我们操作document对象&#xff0c;比如getElementById()方法可以通过某个标签元素的id来获取这个标签元素 // 用法 window.document.getElementById(id); // 例子 <!DOCTYPE html> &l…

LeetCode、374. 猜数字大小【简单,二分】

文章目录 前言LeetCode、374. 猜数字大小【简单&#xff0c;二分】题目及类型思路及代码实现 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领域。 涵盖技…

移动云助力智慧交通数智化升级

智慧交通是在整个交通运输领域充分利用物联网、空间感知、云计算、移动互联网等新一代信息技术&#xff0c;综合运用交通科学、系统方法、人工智能、知识挖掘等理论与工具&#xff0c;以全面感知、深度融合、主动服务、科学决策为目标&#xff0c;推动交通运输更安全、更高效、…

联想拯救者冠名2024第二届OPENAIGC开发者大赛,开启AI落地新纪元

2024年1月17日&#xff0c;在联想拯救者及消费生态新品发布会上&#xff0c;AIGC开放社区携手联想拯救者&#xff0c;宣布将共同举办“AI生成未来第二届拯救者杯OPENAIGC开发者大赛”。此次大赛旨在集结所有开发者的智慧和创造力&#xff0c;推动人工智能技术的创新和应用实践。…

Promise的几道基础题

event loop它的执行顺序&#xff1a; 一开始整个脚本作为一个宏任务执行执行过程中同步代码直接执行&#xff0c;宏任务进入宏任务队列&#xff0c;微任务进入微任务队列当前宏任务执行完出队&#xff0c;检查微任务列表&#xff0c;有则依次执行&#xff0c;直到全部执行完执…

服务器自动拉取git代码运行脚本

# 1.场景分析 工作中常常会遇到本地编辑shell脚本或者python脚本完成后需要在服务器上运行的情况&#xff0c;每次进行拷贝费时费力。下面介绍下通过git管理器&#xff0c;实现本地与服务器代码同步的方式。选择公司搭建的gitlab为例&#xff1a; 2.gitlab配置服务器ssh密钥 …

免费的爬虫软件【2024最新】

在国际市场竞争日益激烈的背景下&#xff0c;国外网站的SEO排名直接关系到网站在搜索引擎中的曝光度和用户点击量。良好的SEO排名能够带来更多的有针对性的流量&#xff0c;提升网站的知名度和竞争力。 二、国外网站SEO排名的三种方法 关键词优化&#xff1a; 关键词优化是SEO…

【IAP】核心开发流程

最近做了IAP U盘升级模块开发&#xff0c;总结下IAP基本开发流程&#xff0c;不深入讨论原理。 详细原理参考 首先需要知道我们需要把之前的APP区域拆一块出来做BOOT升级程序区域。 以STM32F103为例&#xff0c;0x08000000到0x0807FFFF为FLASH空间&#xff0c;即上图代码区域…

Java基础面试题-2day

面向对象 创建一个对象用什么运算符&#xff0c;对象实体和对象引用有什么不同&#xff1f; 创建对象使用new String A new String(); A即为对象引用&#xff0c;通过new运算符&#xff0c;创建String()类型的对象实体。 对象引用的存储位置在栈内存 对象实体的存储位置在堆…

2024玩儿转TikTok之环境介绍及搭建

郑重申明&#xff1a;本文章只对合法合理做tiktok视频运营的用户做学习交流使用&#xff0c;有其他使用不当的违规违法行为后果自负&#xff01; 一、网络环境图介绍&#xff1a;我们只需要保证红色的环境通畅即可(手机阿里tiktok运营专用服务器) 二、服务器部分环境搭建 1、…

STM32F103标准外设库——SysTick系统定时器(八)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;V…

软件测试|sqlalchemy一对一关系详解

简介 SQLAlchemy 是一个强大的 Python ORM&#xff08;对象关系映射&#xff09;库&#xff0c;它允许我们将数据库表映射到 Python 对象&#xff0c;并提供了丰富的关系模型来处理不同类型的关系&#xff0c;包括一对一关系。在本文中&#xff0c;我们将深入探讨 SQLAlchemy …

大数据工作岗位需求分析

前言&#xff1a;随着大数据需求的增多&#xff0c;许多中小公司和团队也新增或扩展了大数据工作岗位&#xff1b;但是却对大数据要做什么和能做什么&#xff0c;没有深入的认识&#xff1b;往往是招了大数据岗位&#xff0c;搭建起基础能力后&#xff0c;就一直处于重复开发和…

基于springboot+vue的校园周边美食探索及分享平台系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目背景…