redis之作为缓存使用

news2024/11/17 15:36:13

写在前面

Redis因为其自身高性能的数据读取能力,因此会经常被应用到缓存的场景中,本文就一起看下Redis当做缓存使用时的特点,问题,以及需要注意的点。

1:缓存的架构模式

从架构模式上来看缓存系统可以分为旁路缓存,内嵌缓存,分别来看下。

1.1:旁路缓存

旁路缓存在系统架构中所处的位置如下图:

在这里插入图片描述

数据读取方式如下:

1:当缓存中有数据时直接读取缓存数据
2:当缓存中没有数据时,从后端存储中读取数据,并将数据写入到缓存中

数据写入方式如下:

1:同时写入缓存和后端存储,保持数据的强一致性
2:数据仅写入后端存储,并是缓存失效

这种方式由应用程序负责管理缓存,即需要在应用程序中写缓存管理相关代码,可能代码如下:

在这里插入图片描述

本文我们要分析的Redis作为缓存使用就是旁路缓存。

1.2:内嵌缓存

内嵌缓存在系统架构中所处的位置如下图:

在这里插入图片描述

数据读取方式如下:

1:当缓存中有数据时,则直接返回数据
2:当缓存中没有数据时,直接从后端存储中读取数据(透读),缓存数据,并返回数据

数据写入方式:

1:数据写入缓存后,缓存以同步(透写)的方式将数据写入到后端存储
2:数据写入缓存后,缓存以异步(后写)的方式将数据写入到后端存储

我们的计算机系统使用的缓存就是内嵌缓存,因为CPU,内存,磁盘的数据存取速度差别很大,所以需要采用缓存机制,三者的时间级别如下图:

在这里插入图片描述

为了避免因为获取下层数据拖慢整个过程,CPU提供了LLC内嵌缓存,用来加速从内存中获取数据,内存提供了page cache用来加速从磁盘中获取数据,此时结构如下图:

在这里插入图片描述

2:缓存的特征

缓存系统主要有以下两个特征:

1:缓存是一个快速子系统,能够提供快速的数据存取服务
2:缓存系统的容量大小一定小于后端慢速系统,即不可能将

对于以上两个特点,1Redis是满足的,2因为Redis提供了数据淘汰策略,所以也是满足的,所以Redis是适合作为缓存系统来使用的。

3:缓存的类型

只读缓存,读写缓存。

3.1:只读缓存

仅仅提供数据读取,如果是数据写入的话,则直接写到后端存储中,缓存中的数据直接失效,下次从后端存储查询后再写到缓存中,如下图:

在这里插入图片描述

3.2:读写缓存

读写缓存是,缓存也接收更新,有如下两种方式:

同步直写:即更新缓存后同时更新后端存储
异步写回:即只更新缓存,后续再更新后端存储

这两种方式如下图:

在这里插入图片描述

同步只写会降低程序的性能,但是保证数据不丢失和一致性,异步写回,可能会导致数据丢失,但是可以提高更新速度,从而提高程序性能。

4:缓存满了怎么办

缓存满了,到底是缓存真的满了,还说说是我们设置的内存太小了!所以在看缓存满了怎么办这个问题之前,首先要来看下如何设置一个合适的缓存大小。

4.1:设置多大的容量

设置容量我们可以参考八二原理,即百分之八十的访问量是由百分之二十的数据贡献的,如下图:

在这里插入图片描述

图中展示的蓝色区域就是20%的数据贡献的80%的访问量,而粉色区域就是剩余的80%的数据贡献的20的访问量,即蓝色区域面积:粉红色区域面积=8:2,当然这只是统计学意义上的一个概念,和我们实际的业务场景还是有一定偏差的,所以结合不同的业务场景,这个20%可以是一个范围,这个范围是15%到30%,兼顾访问性能和内存带来的开销,那么具体应该怎么做呢?我们觉得可以这样,比如10G一共有10G的数据,我们可以先设置为20%,如2G,然后观察其缓存命中率 ,如果命中率特别高,比如达到了95%,则我们就可以适当的降低内存,但是如果缓存命中率不很理想,比如70%,则我们就需要适当的调高内存,一般能达到90%左右就是一个比较理想的状态了。

比如确定了内存为4g则可以通过命令config set maxmemory 4gb来动态调整。那么,缓存大小已经确定了,当缓存满了该怎么办呢?这就需要看下缓存的淘汰策略了。

4.2:缓存的淘汰策略

redis当前一共提供了8中淘汰策略,通过配置项maxmemory-policy来配置,默认是noeviction,其含义是不淘汰,这种策略当缓存满时如果写入数据,会因为无法写入数据而直接返回错误,这里的noeviction也正是redis提供的8中淘汰策略的一种。对于这8中淘汰策略,我们可以按照是不淘汰数据,淘汰数据划分,对于淘汰数据又可以按照从设置了过期时间的数据中淘汰,从所有的数据中淘汰,如下:

不淘汰数据:noeviction
淘汰数据:
    从设置了过期时间的数据中淘汰
        volatile-random
        volatile-lru
        volatile-lfu
        volatile-ttl
    从所有的数据中淘汰
        allkeys-random
        allkeys-lru
        allkeys-lft

可以看到从设置了过期时间的数据中淘汰的淘汰策略相比于从所有的数据中淘汰仅仅多了一个基于过期时间的ttl策略。接下来我们分别看下每种淘汰策略具体的工作方式。

4.2.1:noeviction

不淘汰数据,当空间不足无法写入新数据时,停止对外服务,直接给写入请求返回错误。

4.2.2:volatile-random

从设置了过期时间的数据中随机删除。

4.2.3:volatile-lru

从设置了过期时间的数据中通过lru算法删除,这里介绍下lru(least recent used)算法,该算法通过一个队列维护数据,队列头叫做MRU端(most recent used),对列尾叫LRU(least recent used)端,当某个数据被访问或新写入时,就会被移动到MRU,其他数据后移,如下图:

在这里插入图片描述

但是实际应用时,因为Redis的数据较多,如果对全部数据维护一个大的lru链表的话,会带来额外的空间开销,因此,Redis对该算法进行了优化,具体是首先在RedisObject的元数据中维护lru属性作为数据最近被访问的时间,当需要淘汰数据时,随机的选择的N(通过参数max-samples配置)个数据,然后选择其中lru值最小的进行删除。

4.2.4:volatile-lfu

类似于lru,但是在RedisObject的元数据中又多维护了一个访问次数的信息counter,当淘汰数据时也是随机选择N个数,但是淘汰数据的策略是,优先淘汰访问次数最少的,如果是访问次数相同的话,则淘汰最近一次访问时间最早的(这和lru是一致)

4.2.5:volatile-ttl

从设置了过期时间的数据中选择一个最接近过期时间的删除。

4.2.6:allkeys-random

从所有数据中随机选择一个删除。

4.2.7:allkeys-lru

对所有数据使用lru算法删除,同4.2.3:volatile-lru

4.2.8:allkeys-lfu

4.2.4:volatile-lfu,不同之处是从所有的数据中选择。

4.2.9:怎么选

allkeys-lru:业务数据有明显的冷热数据区分,让最常访问的数据保留下来,提升访问性能。
allkeys-random:业务数据没有明显的冷热数据区分,访问比较均匀
volatle-lru:存在所有用户一定会查看的数据,比如全站的通知,置顶的新闻等,对于这些数据可以不设置过期时间,这样永远不会被删除,其他数据设置过期时间通过lru淘汰

5:如何解决缓存一致性问题

在分析这个问题之前,我们要先看下这里的一致性是什么,主要以下两方面:

1:缓存和数据库中都有数据,二者数据必须一直,且都是最新数据
2:缓存中没有数据时,数据库中的数据必须是最新数据

以上两种的任何一种情况发生我们就说出现了数据不一致。我们知道,Redis一般作为旁路缓存使用,而具体的缓存类型又可以分为只读缓存和读写缓存,关于数据一致性的问题,我们也需要从这两种缓存类型切入进行分析。

5.1:读写缓存的数据一致性问题

我们先来回顾下读写缓存的特点,读写缓存即更新时先更新缓存(目的是提高更新速度),对于后端存储的更新有以下的两种方式:

同步直写策略:写缓存时同步更新后端存储
异步写回策略:写缓存后,异步的写后端存储

其中同步写回策略不会有数据一致性的问题,但是需要引入事务,保证二者操作的原子性。对于异步写回策略,如果是异步更新后端存储失败,则会出现数据一致性问题。所以,对于读写缓存要想解决数据一致性问题,可以采用同步直写策略,或者是增加重试机制。

5.2:只读缓存的数据一致性问题

注意以下分析,基于业务接受短时间内读取到不一致的数据,对于业务不接受任何数据不一致的场景,就需要考虑锁等待。

对于只读缓存我们需要将操作分成两类来进行分析,即新增数据,和删改数据。

5.2.1:新增数据

新增数据时,会直接将数据写到后端存储,此时缓存中是没有数据的,符合缓存中没有数据时,数据库中的数据必须是最新数据的条件,因此此时不存在数据一致性问题。

5.2.2:删改数据

对于删改数据,我们可以分为以下不同的场景来分析。

  • 先更新数据库成功再删除缓存失败
    这种情况是因为缓存没有删除,导致数据库和缓存数据不一致,出现问题的根本原因是后更新缓存失败,因此我们只需要加入重试机制再次尝试删除缓存就行了,具体的可以通过消息队列来实现重试机制,如下图:

在这里插入图片描述

  • 先删除缓存成功后更新数据库失败
    这种情况因为数据库没有更新,所以就会出现了数据一致性问题,如下图:

在这里插入图片描述

那么此时,怎么办呢?出现问题的根本原因是后更新数据库失败,因此我们只需要加入重试机制再次更新数据库就行了。

  • 先删除缓存成功后更新数据库也成功
    这种情况出现数据一致性的场景是,在线程A删除缓存后,更新数据库前的时间段,线程B要获取缓存值,此时线程B发现缓存中没有对应的值,因此就会从数据库中读取,而此时读取到的是还没有更新的值,而后线程B会将该值重新写到缓存中,最后线程A有更新数据库,最终导致缓存中是老值,而数据库中的是新值,此时二者不一致,就导致了数据一致性问题,这个过程可以参考下图:

在这里插入图片描述

出现数据不一致的原因是红框中的操作,此时,我们只需要在一段时间后删除红框中的写入就可以了,即线程A操作完成后sleep一段时间,再一次删除缓存,这个sleep的时长可以通过计算完成数据库读取+写入到缓存的时间估算出来。此时伪代码可能如下:

在这里插入图片描述

  • 先更新数据成功后删除缓存也成功
    这种情况,假设线程A更新数据库后,还没有来得及删除缓存,此时如果是有线程B来读取缓存值,会从缓存中读取到旧值,但是当线程A最终删除缓存后,后续不会有数据一致性的问题,所以这种场景不存在数据一致性的问题。

6:缓存雪崩,缓存击穿,缓存穿透

这3个问题都是因为无法通过缓存获取数据,进而导致请求大量挤压到数据库系统,造成数据库的巨大压力,甚至导致数据库的宕机,进而引发线上事故,接下来分别看下。

6.1:缓存雪崩

缓存雪崩的定义是大量数据无法在缓存中获取,进而导致应用层将请求发送到数据库,给数据库造成巨大的压力。一般缓存雪崩由两方面原因造成,首先是大量的key在同一时间过期,其次是redis实例宕机,针对这两种情况来分别看下如何处理。

6.1.1:大量key同时过期

这种情况参考下图:

在这里插入图片描述

针对大量key同时过期造成的缓存雪崩,我们可以从事前预防和事后补救两方面来进行分析。

  • 事前预防
    既然是大量key同时过期造成,我们只需要注意在程序中不要设置给大量的key设置相同的过期时间就可以了,具体的做法是,设置过期时间时通过增加一段时间的随机值,比如1~3分钟

这种事前预防是业务无损的,实际应用中可以放心的使用。

  • 事后补救
    服务降级,即如果是查询的是核心数据则允许从数据库直接获取,如果查询的是非核心数据则直接返回错误。如下图:

在这里插入图片描述

服务降级的思想就是丢车保帅,断尾自救。

注意:这种方式是业务有损的,实际应用还是要格外小心!

6.1.2:缓存实例宕机

从事前预防和事后补救两方面来分析。

  • 事前预防
    配置主从节点的高可用集群,当主节点宕机后,从节点直接切换为主节点继续提供服务(可通过哨兵 实现)。
  • 事后补救
    服务熔断和服务限流,其中服务熔断是访问缓存的客户端不再访问缓存而是直接返回错误,如下图:

在这里插入图片描述

服务限流是,只允许一定比例的请求执行,剩余的请求直接返回,如下图:

在这里插入图片描述

6.2:缓存击穿

缓存击穿是一些热点数据因为过期被删除导致请求挤压到数据库,如下图:

在这里插入图片描述

这种情况解决方案是,对于会频繁访问的热点数据不要设置过期时间。

6.3:缓存穿透

缓存穿透不同于缓存雪崩和缓存击穿,后二者都是缓存中没有数据,但是在数据库中有数据,但是缓存穿透是在缓存中和数据库中都没有数据,但是效果是一样的,都是请求全部到数据库,给数据库造成巨大的压力,但因为数据库中没有数据,所以其影响要比缓存雪崩和缓存击穿更严重,参考下图:

在这里插入图片描述

造成缓存穿透的原因可能如下:

1:程序错误,导致同时删除了缓存和数据库中的数据
2:恶意攻击,故意访问不存在的数据

对于缓存穿透有如下几种方案。

6.3.1:直接缓存约定值

对于缓存中没有数据库中也没有的值,我们可以和业务方来约定一个值代表这种情况,比如notExitsInCacheAndDb,当返回该值时业务方就知道发生了缓存穿透,可以做特殊处理,并且该约定值也会被缓存到缓存中,下次可以使用。

6.3.2:基于布隆过滤器判断数据库是否存在

直接查询数据库判断是否存在的成本较高,会增加数据库的压力,此时,这个判断过程可以通过布隆过滤器完成,布隆过滤器的工作原理是,使用若干个bit的0和1来表示该值是否存在,而具体是哪几个位则通过对应个数的哈希函数获取,如hash(key)%bit数就是对应的bit的位置,当这若干个bit位有一个为0时,则说明肯定不存在,工作原理可以参考下图:

在这里插入图片描述

因此,我们只需要将数据库中的值,维护到这个bit数组中,后续判断时直接通过布隆过滤器来判断,如果是不存在就不用通过数据库来查询了,减轻了数据库的压力。

6.3.3:恶意请求检测

程序中可以通过参数等信息来判断是否为恶意请求,如果是恶意请求则直接过滤,这种方式和业务关系密切,需要好好考虑下如何做,不然可能出现误杀的情况。

7:缓存污染

7.1:什么是缓存污染

访问次数很少的数据,一直保留在缓存中,占用缓存空间的情况就是缓存污染。即不会或几乎不会再次被访问的数据一只保留在缓存中。

7.2:如何解决缓存污染

缓存污染是不应该继续保留在缓存中数据留在缓存中,因此我们需要通过一定的手段来将其淘汰出缓存,而淘汰缓存就需要用到缓存的淘汰策略,因此这里一定的手段就是配置合适缓存淘汰策略了,接下来我们就针对每种缓存淘汰策略看下其是否适合用来解决缓存污染问题。

  • noeviction

该策略在缓存满时不会淘汰数据,因此不能用来解决缓存污染问题。

  • volatile-random

随机删除,可能删除经常访问的数据,当然也可能删除造成缓存污染的数据,但是随机性太强,也不能用来解决缓存污染问题,并且是只会针对设置了过期时间的数据,如果是没有设置过期时间的话,则无法处理。

  • volatile-lru

如果是都设置了过期时间的话,适合用来解决缓存污染的问题。但是有一种情况不适合,即存在大批量的key单次扫描的情况,此时大量的key的访问时间都很新,也就无法淘汰,但是访问次数都很少,这种场景就需要考虑lfu算法的淘汰策略。

  • volatile-lfu

如果是都设置了过期时间的话,适合用来解决缓存污染问题。

  • volatile-ttl

正常数据的过期时间并不能反映出其是否是造成缓存污染的数据,所以不适合用来解决缓存污染问题,但是如果是业务上清晰的知道哪些数据使用的时间长,哪些数据使用的时间短,并基于此设置了对应有效期的话,则适合用来解决缓存污染的问题。

  • allkeys-lru

类似于volatile-lru,但也无法解决大批量key单次扫描的问题,也需要使用lfu。

  • allkeys-lfu

适合用来解决缓存污染问题。

  • allkeys-random

随机删除数据,作用有限,不能用来解决缓存污染问题。

写在后面

参考文章列表:

缓存的几种架构模式 。

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

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

相关文章

Kotlin 基础学习笔记第八章——高阶函数:Lambda作为形参和返回值

一、声明高阶函数 高阶函数定义:高阶函数就是以另一个函数作为参数或者返回值的函数。 在kotlin中,函数可以用lambda或者函数引用来表示。因此,任何以lambda或者函数引用作为参数的函数,或者返回值为lamda或函数应用的函数&#x…

基于遗传算法优化的lssvm回归预测-附代码

基于遗传算法优化的lssvm回归预测 - 附代码 文章目录基于遗传算法优化的lssvm回归预测 - 附代码1.数据集2.lssvm模型3.基于遗传算法优化的LSSVM4.测试结果5.Matlab代码摘要:为了提高最小二乘支持向量机(lssvm)的回归预测准确率,对…

使用openshift 进行云平台连接

使用openshift 进行云平台连接 OpenShift CLI on Windows openshift 文档地址 OpenShift CLI on Mac 通过Homebrew方式安装 brew install openshift-cli安装完成,进行验证 oc version服务连接 oc login 服务地址根据提示输入用户名和密码,即可连接…

某讯滑块验证码反汇编分析-第一章

某讯滑块验证码反汇编分析-第一章js分析初步分析vmp结构js分析 在滑动完成后,会请求【cap_union_new_verify】接口,其中有5个比较长的参数,看起来可能会有加密的,分别如下。 参数名来源uasesscollecteksvData 那现在js看看这些参…

让车机「下雨」,路特斯的智能座舱跑偏了吗?

HIEV消息(文/张祥威)“我们今天用双8155,都觉得(算力)还不够。”杨平说。 这位路特斯的智能座舱研发负责人,目前带领着一支两百多人的团队,为Eletre这款车操刀座舱智能化。 双8155,指…

Gitee应用(1):快速搭建Git环境在不同电脑同步文件

一、前言 环境:Windows 11 64位 ;vscode:1.73.1 当你要操作在两台不同的电脑上操作,或许经常会遇到一个问题,就是怎么同步同一个或几个文件,以方便你在不同的电脑上进行同个文件内容的创作。 在申请了公司电…

【Java难点攻克】「NIO和内存映射性能提升系列」彻底透析NIO底层的内存映射机制原理与Direct Memory的关系

NIO与内存映射文件 Java类库中的NIO包相对于IO包来说有一个新功能就是 【内存映射文件】,在业务层面的日常开发过程中并不是经常会使用,但是一旦在处理大文件时是比较理想的提高效率的手段,之前已经在基于API和开发实战角度介绍了相关的大文…

Java Math类

JavaMath类\huge{Java \space Math类}Java Math类 Math类 包含执行基本数字运算的方法,Math类没有提供公开的构造器。 MathMathMath类本质就是一个工具类,提供许多方法用于其他类调用,但是无法创建子类对象。 常用方法 ①. abs()取绝对值…

CANoe测试的两种方式Test Module 和 Test Unit对比,你常用哪种呢?

🍅 我是蚂蚁小兵,专注于车载诊断领域,尤其擅长于对CANoe工具的使用🍅 寻找组织 ,答疑解惑,摸鱼聊天,博客源码,点击加入👉【相亲相爱一家人】🍅 玩转CANoe&…

前端接口联调时传的参数是array数组形式处理

情景:使用van-uolaoder组件上传多个图片后调用保存按钮,这时由于存在多张图片,调用接口时前端传的参数就是数组形式。(前端传的数组要先转为字符串) 处理方法是: 1. 先确定后端使用什么注解,如…

Gerb视图支持新表单和旧表单

Gerb视图支持新表单和旧表单 GerbView生产高级软件Companions,但也易于替换、打印和查看HPGL、HPGL/2、Excellon和国际象棋文件。该程序支持RS274D和RS274X。您可以使用此软件将旧格式和标准RS274D转换为具有默认信息的新类型RS274X。Gerbview将致力于展示和绘制您的…

计算机毕业设计django基于python租房系统-房屋租赁系统

项目介绍 在各学校的教学过程中,租房系统是一项非常重要的事情。随着计算机多媒体技术的发展和网络的普及。采用当前流行的B/S模式以及3层架构的设计思想通过Python技术来开发此系统的目的是建立一个配合网络环境的租房系统的平台,这样可以有效地解决租房系统混乱的局面。 本文…

(P3)python类的特殊方法 init、getitem、len、call

python 类的特殊方法 参考资料 python解释器遇到特殊句法时,会去调用特殊方法,这些特殊方法以双下划线开头。 1、__init __ () 该方法在对象创建时被解释器自动调用。 2、__getitem __ () getitem 获取实例对象的具体内容 p[key] 3、__len __() len …

牛客挑战赛65 D.233求min(二维偏序 树状数组/cdq分治)

题目 给出长度为n(n<1e5)的序列a1,a2,...,an​和b1,b2,...,bn(-1e3<ai,bi<1e3)&#xff0c; 请你找出1≤l≤r≤n&#xff0c;使得min(alal1...ar, blbl1...br)最大&#xff0c;并输出这个值 思路来源 官方题解 LYC_music submission 题解 两种做法&#xff0c; …

Python模块和包的介绍(模块含义、用模块好处、模块分类、包和模块的创建)

&#x1f308; 个人主页&#xff1a;王子玉博客 &#x1f506; 免费专栏&#xff1a;Python基础教程、python常用模块 ❤️ 分享网站&#xff1a; 《Python自学网》&#x1f449;&#x1f449;基础入门到逐步深入 | 适合新手入门到精通 | web开发、爬虫、自动化运维、自动化…

yocto 自动挂载分区(基于stmp32mp1)

yocto 自动挂载分区&#xff08;基于stmp32mp1&#xff09; 上一章我们在stm32mp1开发板上实践了创建分区的过程&#xff0c;本节我们再来分析下上小节创建的分区开机时是怎么完成分区的自动挂载的。 答案就在systemd里面&#xff1a; 下面就来详细分析一下这个几个文件 mo…

C/C++入门003-C语言变量与运算符

文章目录变量常量的概念常量的类型变量的概念定义变量为什么要定义变量如何使用变量&#xff1f;变量初始化修改变量变量之间的值传递如何查看变量的值?变量的作用域printf函数scanf函数函数的参数运算符算数运算符赋值运算符sizeof运算符逗号运算符关系运算符逻辑运算符三目运…

攻击类型的攻击次数分布

攻击类型分析 2018 年&#xff0c;主要的攻击类型 1 为 SYN Flood&#xff0c;UDP Flood&#xff0c;ACK Flood&#xff0c;HTTP Flood&#xff0c;HTTPS Flood&#xff0c; 这五大类攻击占了总攻击次数的 96&#xff05;&#xff0c;反射类攻击不足 3%。和 2017 年相比&…

【Linux网络编程】select多路复用

文章目录前言如何增强服务端的通信能力Linux的设计哲学文件描述符代码讲解以文件方式操作命令行阻塞函数与非阻塞函数轮询select()代码全貌The End前言 我们上节课讲解了服务端的编程(Linux服务端编程初体验) 本节课要讲的是select 提示&#xff1a;以下是本篇文章正文内容&am…

RocketMQ引发的磁盘预警复盘

RocketMQ引发的磁盘预警复盘前言发现问题排查过程Step 1Step 2Step 3Step 4Step 5Step 6Step 7Step 8Step 9解决方式写在最后前言 一款优秀的中间件&#xff0c;参数的缺省值必然是经过反复验证得出的最优解&#xff0c;勿动&#xff01; 发现问题 某台SaaS服务器磁盘不足发出…