首页 > 数据库 >redis——P2:对P1的思考

redis——P2:对P1的思考

时间:2024-06-18 19:33:26浏览次数:25  
标签:P2 缓存 请求 数据库 redis 布隆 P1 过滤器

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

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

@Service
class XxxService {
    /* dependencies */
    // Service Code
    public 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的后面再来着重介绍这三个概念,现在扯回来 ↩︎

标签:P2,缓存,请求,数据库,redis,布隆,P1,过滤器
From: https://www.cnblogs.com/pidanhub/p/18254974

相关文章

  • redis——P3:持久化
    虽然缓存功能已经实现,但是作为对外提供服务的软件开发者,不能只关注是否提供了正确的服务,稳定和快速恢复等等指标是同样非常非常重要的。考虑这样一个问题,redis确实因为不可抗力宕机了(假设我喜欢黑框框打开,然后手贱按了^C),于是瞬间redis里所有缓存全没了?这对于一个追求高性能、高可......
  • redis——基础服务
    首先为什么要做一个redis出来?数据库不够用了吗?考虑到原本的应用程序是客户端访问服务端,服务端访问业务数据需要去数据库去拿,而数据库是个持久化的应用程序,是需要磁盘IO的,这就导致了速度会慢,并且如果存在大量的访问,会导致数据库崩溃。除去导致崩溃这样严重且极端的情况,这点性能虽然......
  • 认识Redis
    认识RedisRedis是目前最受欢迎的NoSQL数据库之一;db-engines排名上,所有数据库类型排名第六,键值对数据库排名第一。Redis是用C语言编写的开源、支持多种数据结构、支持网络、基于内存、可选持久性的键值对内存型数据库,具备以下特征:基于内存运行、效率高单线程运行支持分布式......
  • 【面试八股总结】Redis数据结构及底层实现
    一、五种基本数据结构        Redis提供了丰富的数据类型,常见的有五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)结构类型结构可存储值结构读写能力使用命令底层数据结构String字符串、整数或浮点数对字符串或字符串的一部分进行操作,对整数或浮点......
  • 采用java语言+Redis+RabbitMQ开发的 门诊his系统源码 一站式的门诊his系统 门诊业务流
    采用java语言+Redis+RabbitMQ开发的门诊his系统源码一站式的门诊his系统门诊业务流程医院信息系统(HIS系统)门诊业务是医院信息化建设的重要组成部分之一,它涵盖了医院门诊部门涉及的各项业务。HIS系统门诊业务的实施,可以实现医院门诊业务的信息化管理和数据化处理,提高医疗服......
  • 探索Redis的运行情况和数据——一次有趣的Redis旅程【GPT生成】
    探索Redis的运行情况和数据——一次有趣的Redis旅程前言Redis,一个高性能的键值对数据库,广泛应用于缓存、会话管理和实时数据处理。如果你正在使用Redis,你可能会好奇如何检查它的运行情况,以及它究竟存储了哪些数据。在这篇博客中,我将带你一起使用Xshell连接到服务器,探索Redis的奥......
  • Redis常见数据类型及其常用命令详解
    文章目录一、Redis概述二、Redis常用命令1.通用命令1.1KEYS:查看符合模板的所有key1.2DEL:删除一个指定的key1.3EXISTS:判断key是否存在1.4EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除1.5TTL:查看一个key的剩余有效期1.6COPY:复制Redis数据库......
  • 每日一题-24-06-17 (P10218)(加倍!)
    看到异或直接想到线性基和trie很明显是trie从高到低一位位考虑,如果两个儿子都有,想使这一位为1,必须有一个变成加法然后就便利一下trie,记录一下剩余的体力和最小的加法的数就好了#include<bits/stdc++.h>usingnamespacestd;#definell__int128#definelstr[u][0]#define......
  • 每日一题-24-06-17 (P10217)
    今年省选题,考场上竟然没做出来今天似乎直接一眼出来了就是枚举下\(m\)模\(n\)的余数然后解个方程即可#include<bits/stdc++.h>usingnamespacestd;#definelllonglongintT,n,X,Y;intx[100005],y[100005];lls[100005],t[100005],res,k;llsub_down(llx,lly){......
  • C++ Redis 客户端简单使用
    一、准备工作1、Redis语法命令手册:https://c.biancheng.net/redis2/lpush.html2、下载我的RedisDemo示例(包含了redis源码):https://download.csdn.net/download/weixin_45075013/89446384?spm=1001.2014.3001.5503二、Redis客户端连接与使用......