• Redis常见场景问题和解决方案


    缓存穿透(查不到数据)

    概述

    当用户想要查询一个数据,发现Redis中不存在,也就是所谓的缓存没有命中,于是这个数据请求就会打到数据库中。结果数据库中也不存在这条数据,那么结果就是什么都没查询出来。那么当用户很多时候的查询,缓存中都没有数据,请求直接打到数据库中,这样就会给数据库造成很大的压力,缓存的作用也就几近于失效了,那么这种情况就叫做缓存穿透。

    解决方案

    方案一:保存空值

    当数据库中也查询不到数据时,那么将返回的空对象也缓存起来,同时设置一个过期时间,之后再访问这个数据将会从缓存中获取,从而起到保护数据库的作用。

    例如:查询userId=100的用户信息(key=[userId],value=[用户json]),那么如果缓存和DB中都不存在,则在缓存中保存一条key=100,value=""的数据,那么用户再查询userId=100的时候,就直接可以返回空了。不需要查询DB。

    方案二:布隆过滤器

    步骤1:将数据库所有的数据加载到布隆过滤器。

    步骤2:当有请求来的时候先去布隆过滤器查询,判断查询的数据是否存在。

    步骤3:如果Bloom Filter判断数据不存在,那么直接返回空给客户端。

    步骤4:如果Bloom Filter判断数据存在,那么则查询缓存DB

    步骤5:将DB中查询的结果返回给客户端(并且缓存到Redis中)

    缓存击穿(高并发查询某数据,且缓存过期)

    概述

    指一个非常热点的key,在不停的高并发请求着,那么当这个key在缓存中失效的一瞬间,持续对这个key的高并发就击穿了缓存,直接请求到了数据库,就像在一个屏障上早开了一个洞

    当热点key过期失效的一瞬间,高并发突然融入,会对数据库突然造成巨大的压力,严重的情况甚至会造成数据库宕机。

    解决方案

    方案一:设置热点数据永不过期

    从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后所产生的缓存击穿问题。

    方案二:加互斥锁

    使用分布式锁,当缓存数据过期后,保证对每个热点key同时只有一个线程去查询后端服务,并将热点数据添加到缓存。

    缓存雪崩(缓存大批量失效或Redis宕机)

    概述

    指在某一个时间段,缓存集中过期失效,或Redis宕机,导致针对这批数据的查询都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。

    其实缓存集中过期,倒不是最致命的,比较致命的是Redis发生节点宕机或断网。因为缓存集中过期后,数据库压力增大,但是随着缓存的创建,压力也会逐渐变小。但是Redis服务节点宕机,对数据库服务器造成的压力是不可预知的,很有可能是持续压力而最终造成数据库宕机。

    解决方案

    方案一:配置Redis集群

    通过配置Redis集群,提升高可用性,那么即使挂掉几个Redis节点,集群内的其他Redis节点依然可以继续对外提供服务。

    方案二:限流降级

    缓存失效后,通过加锁队列来控制读取数据库且写入缓存的线程数量。

    方案三:数据预热分散过期时间

    在正式部署之前,先把可能被高频访问的数据预先访问一遍,这样大部分热点数据就加载到缓存中了,并且通过设置不同的过期时间,让缓存失效的时间尽量均匀,防止同一时刻大批量缓存失效。

    布隆过滤器

    概念介绍

    布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率删除困难

    布隆过滤器的特征是:它可以判断某个数据一定不存在,但是无法判断一定存在。(确实有点拗口,但当我们介绍完它的原理,就很容易明白了)

    使用场景

    场景一:原本有10亿个号码,现在又来了10万个号码,如何快速判断这10万个号码是否在10亿个号码库中?

    场景二:需要爬虫的网站千千万万,对于一个新的网站url,我们如何判断这个url我们是否已经爬过了?

    解决方案

    针对上面的需求,我们一般会想到两种解决方案:

    方案一:将10亿个号码存入数据库中,进行数据库查询,准确性有了,但是速度会比较慢。

    方案二:将10亿号码放入内存中,比如Redis缓存中,这里我们算一下占用内存大小:10亿*8字节=8GB,通过内存查询,准确性和速度都有了,但是大约8GB的内存空间,挺浪费内存空间的。

    那么对于类似这种,海量数据集合,如何准确快速的判断某个数据是否在大数据量集合中,并且不占用内存?那么布隆过滤器应运而生了。

    实现原理

    布隆过滤器是什么?

    它是一种数据结构,是由一串很长的二进制向量组成,也可以将其看成一个二进制数组。既然是二进制,那么里面存放的不是0,就是1,但是初始默认值都是0。如下图所示:

    添加数据如何处理?

    当要插入一个元素时,将其数据分别输入k个哈希函数,产生k个哈希值。以哈希值作为位数组中的下标,将所有k个对应的比特置为1

    比如,下图hash1(x)=1,那么在第2个格子将0变为1(数组是从0开始计数的),hash2(x)=6,那么将第5个格子置为1hash3(x)=16,那么将第16个格子置位1,依次类推。如下图所示(只演示hash1~hash3):

    如何判断数据是否存在?

    知道了如何向布隆过滤器中添加一个数据,那么新来一个数据,我们如何判断其是否存在于这个布隆过滤器中呢?

    很简单,我们只需要将这个新的数据通过上面自定义的几个哈希函数,分别算出各个值,然后看其对应的地方是否都是1,如果存在一个不是1的情况,那么我们可以说,该新数据一定不存在于这个布隆过滤器中

    那么反过来说,如果通过哈希函数算出来的值,对应的地方都是1,那么我们能够肯定的得出:这个数据一定存在于这个布隆过滤器中吗?

    答案是否定的,因为多个不同的数据通过hash函数算出来的结果是会有重复的,所以会存在某个位置是别的数据通过hash函数置为的1。即:“假阳性”(false positive)如下图所示:

    今天的文章内容就这些了!


     

  • 相关阅读:
    IPv6简介
    MySQL中的空值 Null 和空字符‘‘
    Golang源码:singleflight分析(二)
    vue3新一代状态管理器 — pinia的学习与使用
    【英语】常见连音规则
    深度学习实战06-循环神经网络(RNN)实现股票预测
    从底层原理看Android的序列化是如何实现的
    centos图形化桌面中火狐浏览器无法访问项目页面问题处理
    cpp primer plus笔记06-函数
    copilot 连接问题
  • 原文地址:https://blog.csdn.net/m0_73311735/article/details/126483508