redis——P2:对P1的思考

news/2024/9/30 11:32:32

到P1结束,redis都已经是一个不错的服务了,具体体现在缓存应用程序需要的数据,甚至在内存爆满的条件下还可以提供服务,似乎目的已经达成。但是实际上可能会遇到一些极端的情况,比如宕机。如果redis宕机了怎么办?目前所有的数据都存储在内存当中,宕机意为着失去所有缓存的数据。前面说过我理解的真正需要用到redis的应用程序,它们一定有大量的查询请求,可能造成后面躲着的数据库服务扛不住。

其实写到P1结束的时候,我突然意识到一个问题:在平时写的系统中,redis服务挂了,那些请求redis的API会抛出异常,这将导致明明还有数据库可以承担业务,但是用户的请求会全部异常,为什么没人捕获这个异常,继续向数据库请求呢?

@Service
class XxxService {/* dependencies */// Service Codepublic Xxx xxxMethod () {Xxx xxx = null;if (/*redis opertions*/) {/* xxx = ...*/}if (xxx == null) {/* database opertions */}return xxx;}
}

基于这个问题,去检索了一下,结果在知乎上还真的有讨论帖,里面的回答都很有道理:分布式系统中Redis等缓存系统宕机应不应该影响正常应用系统运行? - 知乎 (zhihu.com)

已知如果连接失败,则如果全局捕获了异常,则不会使系统宕机。

这个标题就让我更迷惑了:为什么是该不该影响?难道还有第二个选项:redis宕机了数据库能用也不用?这似乎不合常理,但仔细一想,也不是没有道理的,下面就来探讨一下这个问题[1]。不过前提,实际业务场景仍是要具体问题具体分析,这是永恒不变的,这里讨论的也只是某些情况。

回到为什么要加缓存这个问题,性能瓶颈是一方面,我认为保护系统(高可用)是另外一个重要的原因:假如请求的数量数据库可以支撑,那么就用数据库。但是如果大量的请求会击垮数据库呢?这就不止一个两个接口会无法使用,可能整个系统都无法使用!绝不可以一拍脑袋就将压力降下去,这是危险的。这个时候可以采取的方案有:

  • 可以访问数据库,但开启熔断,使请求的数量维持在数据库可以接收的范围内;
  • 放弃提供服务,抓紧抢修redis服务,使redis服务重新上线;
  • 使用redis集群,在redis宕机时,让备用节点顶替,自动恢复,不过这个是需要更多的成本(得加钱)

如果redis服务在系统当中很重要(架构师在技术选型时已经确认很重要),那么对它的使用就要做好备用方案。

基于上面的问题,redis和数据库之间有三个常见的概念,涉及到请求的数据redis没有缓存:

缓存穿透

数据库里面没有这个数据(假如说是10),自然缓存也不存在,那么每次访问10的情况就都是:访问10->缓存没有,去数据库拿->数据库没有(没有也没办法写到缓存)。每次访问10都会造成资源浪费。

image-20240517100857695

利用这,攻击者就可以制造大量的请求去攻击应用程序背后的db,比如攻击者可以获取user_id的格式,构造大量的根本不存在在数据库的请求(这一步甚至可以构造合法的请求),那么保护在数据库前的缓存的作用就消失了,拖慢系统的性能甚至让数据库宕机。这就是缓存穿透攻击

解决方案

  • 对请求的参数进行校验,不合法的参数直接忽略。不过上面也提到,这个是可以被绕过的,不过只要数据的格式隐藏的够深让人不好猜到,也能起到奇效。比如参数是123456789,但是如果12和89是加的前缀和后缀,那么只要攻击者不知道这个规则,就可以防御。

  • 缓存空值,如果请求在数据库不存在,可以在缓存中存入一个特殊值代表没有,比如说“null”。这样也有坏处,如果攻击者构建了大量的不存在的键,则会给缓存带来很大压力,可能会导致新的缓存不能存入(noeviction内存淘汰策略),或频繁的进行内存淘汰、删除过期键时间长等等。

  • 密码学防御,读者可以参考缓存穿透和密码学防御思路 - FreeBuf网络安全行业门户。

  • 布隆过滤器(着重介绍)

    布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。布隆过滤器可以告诉我们 “某样东西一定不存在或者可能存在”,也就是说布隆过滤器说这个数不存在则一定不存,布隆过滤器说这个数存在可能不存在(即误判)。

    利用这个判断是否存在的特点可以做很多有趣的事情。

    • 解决Redis缓存穿透问题(面试重点)
    • 邮件过滤,使用布隆过滤器来做邮件黑名单过滤
    • 对爬虫网址进行过滤,爬过的不再爬
    • 解决新闻推荐过的不再推荐(类似抖音刷过的往下滑动不再刷到)
    • HBase\RocksDB\LevelDB等数据库内置布隆过滤器,用于判断数据是否存在,可以减少数据库的IO请求

    这张图可以解释布隆过滤器的原理,二进制向量和散列函数。图中是一个两个字节大小的向量,有两个元素,三个散列函数,根据散列函数计算出来的值将对应的位置1。将来想要判断值是否存在的时候,需要判断这三个散列函数计算的值的在向量中的位是否都是1,如果都是,则证明这个值可能存在,否则一定不存在

    image-20240517104346514

    为什么是可能存在,其实也好理解,如图:

    image-20240517105438015

    现在很凑巧,有一个请求查询的键为k3,根据散列函数计算结果如图所示,但是它在数据库中实际是不存在的!(即符合缓存穿透,缓存和数据库中都没有这个键)理想情况下,我们希望布隆过滤器可以识别这是个不存在的键,可惜这一次拦截失败了,布隆过滤器误判了,认为这个键存在,也即出现了一次“缓存穿透”。另外,当布隆过滤器中存的值越来越多,即置1的位越来越多,也会带来误判几率的增加。这从直觉上就可以解释。

    但是即便如此,布隆过滤器的性能依旧是很优秀的,可以用于亿级数据的过滤,其性能可见一斑。此外,我们还可以通过数学计算及合理的配置来降低误判的几率。如何做到?Java对布隆过滤器的依赖包里有一个fpp(误判率)的参数,通过设置可以让误判率尽可能接近这个值。实际上,底层也是通过增加位数组的大小和使用更多的哈希函数来降低误判率的,不过这也会带来时空复杂度的消耗。增加位数组的长度自然可以让设置的位更加分散,增加哈希函数可以让键的匹配更精准,但是过多的哈希函数也会让一个键置1的位变多,可能也会增加误判率。实际开发中可以选择使用提供的布隆过滤器的API来控制误判率,如果想要自己实现,就要合理地平衡哈希函数的个数、数组长度和数据量之间的选值。至于个数应该怎么选,我没有很好的头绪,可能数学推导和统计实践可以计算出一个好的参数组合,如同JDK中HashMap的平衡因子那样,从实践中得到一个性能较好的数值。

    原文链接:https://blog.csdn.net/qq_41125219/article/details/119982158

缓存击穿

缓存击穿,从名字上就感觉比缓存穿透要严重得多。这个名词针对的是热点key的场景。众所周知,redis的key可以设置过期时间,但是在大多数项目中,都会存在“热点数据”,即并发访问量很大的数据。缓存击穿中,我们指的都是单个热点key突然失效,导致大量的请求一瞬间全部击打在数据库上,导致数据库瞬间访问量激增,瞬时压力暴增甚至崩溃。

解决方案

根据业务的场景可以选择合适的解决方案,如热点数据永不过期控制访问量,如熔断互斥访问数据库(不好,低性能)。

缓存雪崩

缓存雪崩的名字又比缓存击穿恐怖一些,这次指的是大量的热点key几乎同一时间过期,依然是数据库瞬时压力激增崩溃,甚至重启以后还可能会被打到崩溃的严重现象,这叫缓存雪崩。还蛮好理解记忆的。

解决方案

解决缓存雪崩,主要的矛盾是不能让大量的热点数据在同一时间过期。那么,可以给每一个热点数据的过期时间上加上一些随机的值,让过期时间分散开。同时,熔断策略依然是有效的。还有为了防止因为redis服务宕机导致瞬时压力增加,还需要为redis提高容灾能力,如构建redis集群。当然,为数据库提高容灾能力是治标不治本的策略。

数据库中的缓存

详解MySQL中的Buffer Pool,深入底层带你搞懂它!-腾讯云开发者社区-腾讯云 (tencent.com)


  1. 这涉及到一个大家在准备后端的面试前一般会涉及到的概念——缓存击穿。这个概念一般与另外两个概念——缓存穿透缓存雪崩有关联,为了不影响行文的连续性,我将会在P2的后面再来着重介绍这三个概念,现在扯回来 ↩︎

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

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

相关文章

会议预约管理信息系统

1、项目背景: 会议是企业进行决策、协商的重要组织形式,是企业日常办公处理事务的重要手段,是办公流程中不可缺少的重要环节,作为企业,如何有效的进行会议组织,管理各种会议文档和会议资源,是关系到企业领导进行日常企业运作处理的大事。提高会议效率、发挥会议功能、规…

量化交易:Dual Thrust策略

哈喽,大家好,我是木头左!Dual Thrust策略起源于20世纪80年代,由美国著名交易员和金融作家Larry Williams首次提出。这一策略的核心思想是通过捕捉市场中的短期波动来实现盈利。Larry Williams通过多年的研究和实践,发现市场中存在一种周期性的波动模式,通过这种模式可以预…

解决VSCode中Debug和运行路径不一致

哈喽,大家好,我是木头左!当尝试调试程序时,程序的运行路径与预期不符。这通常会导致程序无法正确读取文件或访问资源,从而影响调试过程。为了解决这个问题,可以在launch.json文件中配置CWD参数,以确保Debug和运行路径一致。 配置launch.json文件 launch.json文件是VSCod…

redis——P1:理解

博主自学redis所用,欢迎交流讨论首先为什么要做一个redis出来?数据库不够用了吗?考虑到原本的应用程序是客户端访问服务端,服务端访问业务数据需要去数据库去拿,而数据库是个持久化的应用程序,是需要磁盘IO的,这就导致了速度会慢,并且如果存在大量的访问,会导致数据库…

接口测试:Mock 的价值与意义

简介 做测试的过程中,对于一些不容易构造、不容易获取的对象,用一个虚拟的对象来替代它,来达到相同的效果,这个虚拟的对象就是 Mock。当做测试的时候,如果后端某些接口还不成熟、所依赖的接口不稳定或者所依赖的接口为第三方接口、构造依赖的接口数据太复杂等等这些问题的…

【CC2DX引擎分析】Action动作的执行流程源码分析

cocos2dx内Action动作的管理与执行流程在引擎源码上的分析。 本文旨在自己对cocos2dx引擎学习的一个笔记总结,对Action动作源码进行分析,加深对动作执行流程的把握,学习架构并之后更好的提高代码质量。 分析总览 从main函数中的Application::getInstance()->run();开始作…

实验一 客户端脚本编程

一、实验目的 通过设计一个个人主页网站,学习常用的HTML标记,学习使用CSS对页面进行美化,掌握JavaScript的语法和常用的浏览器对象,初步学会使用Eclipse创建网站和编辑网页的方法。 二、实验内容和要求 1) 自己设计网页内容,做一张展示自己网页。要求展示的主要内容有:基…

模拟集成电路设计系列博客——7.2.1 流水线ADC基本介绍

7.2.1 流水线ADC基本介绍 流水线ADC和逐次比较型ADC类似,通过迭代搜索查找能够精确反应模拟输入信号的数字码。但是,相比较于通过单个模拟电路执行迭代,流水线ADC有着独立的模拟级来专门执行每次转换。流水线ADC的信号流如下图所示:所有的模拟级在每个时钟周期时进行工作,…