高性能系统架构设计之:多级缓存

前言

        为了提高系统的性能,一般会引入“缓存机制”,将部分热点数据存入缓存中,用空间换取时间,以达到快速响应的目的。

        其实,缓存的应用远远不止存在于服务层(传统的Redis缓存),从客户端发起请求开始,经过域名服务器(DNS) → 内容分发服务器(CDN) → 反向代理服务器(Nginx),然后到达我们的分布式系统(ES),再经过分布式缓存服务(Redis、memcache) → 线程内缓存(Spring-cache、guava-cache),最后到达数据库(RDS),整个链路中每个节点都可以使用缓存,这就是所谓的“多级缓存”。其中缓存策略,算法也是层出不穷,今天就带大家走进缓存。

相关文章:

  • 关于:Ngnix的搭建,参数,复杂均衡,反向代理和调优讲解【篇】(专题汇总)
  • Guava Cache 原理分析与最佳实践
  •  Redis 3.0 的六种缓存淘汰策略

参考文章:

  • 性能为王:微服务架构中的多级缓存设计

正文

一、两种方式

1.1 传统缓存方式

        考虑到 mysql 的性能瓶颈,传统方案中,只在服务层做一级缓存。

  • 当请求到达 tomcat 后,先去 Redis 中获取缓存,不命中再去 mysql 中获取。
  • 当 mysql 成功获取数据以后,返回给前端的同时,将数据缓存进 Redis 一份,以便下次请求命中。

1.2 多级缓存方式

        多级缓存方案利用请求处理的每个环节,分别添加缓存,使最终到达 tomcat 的请求并发数远小于传统方案,达到减轻服务器压力,提升服务性能的目的。


二、多级缓存介绍

        请收好下面的图 - “多级缓存架构总览”,下面将围绕这个架构展开,分别介绍一下每层缓存的使用:

2.1 客户端(HTTP)缓存

        当用户通过浏览器请求服务器的时候,会发起 HTTP 请求,如果对每次 HTTP 请求进行缓存,那么可以减少应用服务器的压力。

        当第一次请求的时候,浏览器本地缓存库没有缓存数据,会从服务器取数据,并且放到浏览器的缓存库中,下次再进行请求的时候会根据缓存的策略来读取本地或者服务的信息。

         一般信息的传递通过 HTTP 请求头 Header 来传递。目前比较常见的缓存方式有两种,分别是:

  • 强制缓存

        当浏览器本地缓存库保存了缓存信息,在缓存数据未失效的情况下,可以直接使用缓存数据。否则就需要重新获取数据。

        在 HTTP 1.1 会使用 Cache-Control 来完成这样的功能,Cache-Control 中有个 max-age 属性,单位是秒,用来表示缓存内容在客户端的过期时间。客户端第一次请求完后,将数据放入本地缓存。那么在 max-age 以内客户端再发送请求,都不会请求应用服务器,而是从本地缓存中直接返回数据。如果两次请求相隔时间超过了 max-age,那么就需要通过服务器获取数据。

  • 对比缓存

        需要对比前后两次的缓存标志来判断是否使用缓存。

        浏览器第一次请求时,服务器会将缓存标识与数据一起返回,浏览器将二者备份至本地缓存库中。浏览器再次请求时,将备份的缓存标识发送给服务器。服务器根据缓存标识进行判断,如果判断数据没有发生变化,把判断成功的 304 状态码发给浏览器这时浏览器就可以使用缓存的数据来。服务器返回的就只是 Header,不包含 Body,这样会很大程度上节约了带宽。     

2.2 CDN缓存

        CDN(Content Delivery Network),即内容分发网络,依靠部署在各地的边缘服务器,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。

        CDN 主要缓存对象是静态数据。如果在客户端和服务器之间再加上一层 CDN,可以让 CDN 为应用服务器提供缓存,当命中CDN缓存时,就不用再请求应用服务器了。

       注: HTTP 缓存提到的两种策略同样可以在 CDN 服务器执行。

        在互联网应用中,因为 CDN 设计多地域多节点组网前期投入成本较高,所以更多的中小型企业会可以选择阿里云、腾讯云等提供的CDN服务。

2.3 Nginx缓存

        说完客户端(HTTP)缓存和 CDN 缓存,我们离应用服务越来越近了,在到达应用服务之前,请求还要经过负载均衡器 。

        虽说它的主要工作是对应用服务器进行负载均衡,但是它也可以作缓存。可以把一些修改频率不高的数据缓存在这里,例如:用户信息,配置信息。通过服务定期刷新这个缓存就行了。

        以 Nginx 为例,Nginx 是一款跨平台的,高性能的 Web 服务器,支持反向代理,负载均衡以及缓存功能。下面,来看看它是如何工作的:

  • 用户请求在达到应用服务器之前,会先访问 Nginx 负载均衡器;
  • 如果发现有缓存信息,直接返回给用户;
  • 如果没有发现缓存信息,Nginx 回源到应用服务器获取信息;
  • 另外,可以设置一个缓存更新服务,定期把应用服务器中相对稳定的信息更新到 Nginx 本地缓存中。

        相关配置可参考下面:

 2.4 进程内缓存

        进程内缓存,是在应用中开辟一块内存空间,数据在运行时被存入这块内存,通过本地内存低延迟、高吞吐的特性提高程序的访问速度。由于其运行在内存中,对数据的响应速度很快,通常我们会把热点数据放在这里。

        目前比较流行的实现:

  • 框架的:Mybatis 框架的一二级缓存,SpringMVC 的页面缓存等;
  • 进程内的:Ehcache、GuavaCache、Caffeine。

        本地缓存的特点:

  • 优点:读取本地内存,没有网络开销,速度更快;
  • 缺点:存储容量有限,可靠性低(如重启后丢失),无法在集群中共享;
  • 场景:性能要求高,缓存数据量少的地方。

        由于目前的系统架构都是分布式的,即:一个服务被部署在多台机器上以实现高性能,而进程内缓存只能存在于当前服务器,所以就会存在进程内缓存数据一致性的问题,如何保障?可以采用 RocketMQ 实现消息的最终一致性方案:        

 2.5 分布式缓存(进程外缓存)

        与进程内缓存不同,进程外缓存在应用运行的进程之外,它可以部署到不同的物理节点,并且拥有更大的缓存容量,通常会用分布式缓存的方式实现,如:Redis集群。

        分布式缓存是与应用分离的缓存服务,最大的特点是:自身是一个独立的应用/服务,与本地应用隔离,多个应用可直接共享一个或者多个缓存应用/服务。

        为了提高缓存的可用性,使部分节点失败或者大部分节点无法通信的情况下集群仍然可用,Redis集群使用了主从复制模型,每个节点都会有 N-1 个复制品(假设:一共有 N 个节点,则每个节点有一个 Master 和 N-1 个 Slave)。当缓存数据写入 Master 节点的时候,会同时同步一份到 Slave 节点。一旦 Master 节点失效,可以通过代理直接切换到 Slave 节点,这时 Slave 节点就变成了 Master 节点,保证缓存的正常工作。

        在 Redis 集群中,因为缓存也是分布式部署的,这样就会产生一个问题:数据根据怎样的规律分配到每个缓存应用/服务上?这里介绍三种缓存数据分片的算法:

  • 哈希算法

        这个算法很好理解,就是对数据记录的关键值进行 Hash 运算,然后再对需要分片的缓存节点个数进行取模,利用得到的余数进行数据分配。Hash 算法是某种程度上的平均放置,策略比较简单。但是它有一个很大的不足:如果要增加缓存节点,对已经存在的数据会有较大的变动,因为节点个数变了,取模后的结果也就不同了。

  • Range Based 算法

        和哈希算法类似,这种方式是按照关键值(例如 ID)将数据划分成不同的区间,每个缓存节点负责一个或者多个区间,相当于预设好了区间。

        例如:存在三个缓存节点分别是 N1,N2,N3。他们用来存放数据的区间分别是,N1(0, 100], N2(100, 200], N3(300, 400]。那么,数据根据自己 ID 作为关键字做 Hash 以后的结果就会分别对应放到这几个区域里面了。

  • 一致性哈希算法

        上面的2种方式似乎都不能解决缓存节点增减带来的问题,所以 Redis 集群引入了一致性 Hash算法(哈希槽)的概念 。一致性hash算法就是为了节点数目发生改变时,尽可能少的数据迁移而出现的。

        Redis 集群有16384(2的32次方)个哈希槽,将数据按照特征值映射到一个首尾相接的 Hash 环上,同时也将缓存节点映射到这个环上。

        每个key通过CRC16校验后对16384取模来决定放置哪个槽,这些值按照顺序在环上排列,集群的每个节点负责一部分hash槽。当需要增减缓存节点的时候,只会变动节点前后的部分数据,其他的数据不受影响,以此来将影响降到最低。

在这里插入图片描述


三、缓存的优缺点

        一句话概况:更快读写的存储介质+减少IO+减少CPU计算=性能优化

3.1 缓存带来的好处

        显而易见,缓存给我们带来最直接的体验就是“快”,我们来总结一下:

  • 通过减少IO(包括磁盘和网络)来提高吞吐量,减少计算量(CPU计算)释放CPU;

  • 通过缩短访问链路,减小访问时间,降低服务器和DB的压力,以达到高性能、高可用的目的;

  • 通过切面的处理方式,可以在各层进行插拔,是所有性能优化最简单有效的解决方案。

        对于不熟悉业务代码或算法的优化者,显然加一层缓存的复杂度和风险更低,而这一层看似简单的缓存,它给系统带来的性能优化有可能大大超过前者。

3.2 缓存带来的困扰

        我们不能否认缓存给我们带来诸多便利,同时,我们不能忽略缓存确实也给我们带来了不少困扰:

  • 数据的一致性、实时性受影响:需要对数据的一致性,时效性进行评估,进而确定是否要缓存或设定缓存的过期时间,比如个性化的数据是否值得缓存?

  • 缓存介质带来的不可靠性:一般使用内存做缓存的话,若机器故障,如何保证缓存的高可用?可考虑对缓存进行分布式做成高可用,同时,需要接受这种不可靠不安全会给数据带来的问题,在异常情况下进行补偿处理,定期持久化等方式。

  • 缓存的数据使得更难排查问题:因为缓存命中是随着访问随时变化的,缓存的行为难以重现,使得出现BUG很难排查。

  • 进程内缓存可能会增加GC压力:在具有垃圾收集功能的语言中(如Java),大量长寿命的缓存对象会增加垃圾收集的时间和次数。

        所以,在使用缓存之前我们需要对数据进行分类,对访问行为进行预估,思考哪些数据需要缓存,缓存时需要采用什么策略?这样,我们才不被缓存所困扰,才能规避这些问题。


四、缓存的适用场景

        在实际应用中,我们需要对数据进行分类,才能更好的使用缓存以及一些策略来辅助。比如:哪些为冷热数据?哪些数据量很大,读取会严重影响IO?哪些数据查多改少(日志数据,爬虫数据)?哪些数据又是经过很复杂的计算得到的结果(这些珍贵的数据需要好好保存利用)?等等。

  • 情况一:缓存数据比较稳定

        如:邮政编码,省市区编码,地域区块,归档的历史数据,这类信息适合通过多级缓存Redis来减少数据库的压力。

  • 情况二:瞬间产生高并发的场景

        如:春晚抢红包,双十一活动,整点秒杀等,存在瞬间的流量洪峰,可以通过多级缓存防止Redis击穿和穿透。

  • 情况三:一定程度上允许数据不一致

        如:库存,余额这种需要强一致性的数据,在分布式系统中需要分布式事务控制,但是如果允许数据段时间的有差别,可以使用先使用缓存,最后利用RocketMQ或定时任务实现最终一致。


总结

        大流量下的多级缓存设计,大致有五大策略,从用户请求开始到数据库依次是:HTTP 缓存,CDN 缓存,Ngginx负载均衡缓存,Cache进程内缓存,Redis分布式缓存,其中:

  • CDN 缓存和 HTTP 缓存是好搭档,他们主要缓存静态数据,将静态资源放在距离用户最近的地方;
  • Nginx 负载均衡器缓存相对稳定的资源,需要服务协助工作(如:设置一个缓存更新服务,定期把应用服务器中相对稳定的信息更新到 Nginx 本地缓存中),毕竟负载均衡才是Nginx的本职工作;
  • Cache 进程内缓存,效率高,但容量有限制,只有性能要求极高,又不需要数据强一致性的场景才适合使用。进程内缓存需要注意数据一致性的问题,利用 RocketMQ 可以实现数据最终一致。
  • 分布式缓存容量大,能力强,牢记三个性能算法并且防范三个缓存风险(缓存穿透,缓存击穿,缓存雪崩)。

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

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

相关文章

虚拟试衣人像合成新SOTA!IMAGDressing-v1:ControlNet和IP-Adapter的最佳拍档

文章链接:https://arxiv.org/pdf/2407.12705 github链接:https://imagdressing.github.io/ Demo试用:https://sf.dictdoc.site/ 亮点直击 为商家引入了一项新的虚拟试衣(VD)任务,并设计了一个综合亲和力测量…

最新缺失msvcp140.dll的多种解决方法,有效解决电脑dll问题

msvcp140.dll 是一个关键的动态链接库(DLL)文件,属于 Microsoft Visual C 2015 Redistributable 的一部分。它为使用 Microsoft Visual C 编译的应用程序提供了运行时支持,确保这些应用程序能够正常运行。以下是对 msvcp140.dll 的…

《0基础》学习Python——第十九讲__爬虫\<2>

一、用get请求爬取一般网页 首先由上节课我们可以找到URL、请求方式、User-Agent以及content-type 即:在所在浏览器页面按下F12键,之后点击网路-刷新,找到第一条双击打开标头即可查看上述所有内容,将上述URL、User-Agent所对应的…

java Builder模式构建对象

Builder 模式是一种创建对象的设计模式,它通过使用多个简单的对象一步一步构建复杂对象。这种模式可以帮助构建复杂的对象,并提供更好的代码可读性和灵活性。Builder 模式特别适用于那些构造函数参数过多或参数默认值较多的类。 下面是一个使用 Builder…

ABAP打印WORD的解决方案

客户要求按照固定格式输出到WORD模板中,目前OLE和DOI研究了均不太适合用于这种需求。 cl_docx_document类可以将WORD转化为XML文件,利用替换字符串方法将文档内容进行填充同 时不破坏WORD现有格式。 首先需要将WORD的单元格用各种预定义的字符进行填充…

【重走编程路】设计模式概述(十二) -- 访问者模式、中介者模式、解释器模式

文章目录 前言21. 访问者模式(Visitor)定义问题解决方案应用场景优缺点 22. 中介者模式(Mediator)定义要解决的问题解决方案应用场景优缺点 23. 解释器模式(Interpreter)定义解决方案应用场景优缺点 前言 …

数据仓库事实表

数据仓库中的三种常见事实表类型:事务事实表、周期快照事实表和累积快照事实表 事务事实表: 事务事实表是记录事务级别数据的事实表。它记录了每个事务发生的具体度量指标,如销售金额、数量等。事务事实表的优势在于能够提供详细的事务级别…

四个节点即可实现的ComfyUI批量抠图工作流

原文链接:ComfyUI面部修复完全指南 (chinaz.com) 下图就是批量抠图的工作流 虽然工作流很简单,但是我们前提还是需要安装好我们的节点 首先安装我们的抠图节点 安装 BiRefNet 所需依赖:timm,如已安装无需运行 requirements.txt…

苹果电脑crossover怎么下载 苹果电脑下载crossover对电脑有影响吗 MacBook下载crossover软件

CodeWeavers 发布了 CrossOver 24 版本更新,不仅兼容更多应用和游戏,得益于 Wine 9.0 带来的 7000 多项改进,CrossOver 还可以在 64 位系统上运行Windows应用的软件,使得用户可以在Mac系统中轻松安装使用仅支持Windows系统运营环境…

java设计模式:03-04-装饰器模式

装饰器模式(Decorator Pattern) 装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰器模式通过创建一个装饰类来包装原有的类,…

视觉探秘:sklearn中聚类标签的可视化之道

视觉探秘:sklearn中聚类标签的可视化之道 在数据科学领域,聚类分析是一种无监督学习方法,用于将数据集中的样本划分为若干个组或“簇”,使得同一组内的样本相似度高,而不同组之间的样本相似度低。Scikit-Learn&#x…

如何在Java、Python、PHP中使用短信推广API?

短信推广API是一种用于营销和推广目的的应用程序接口。该API允许开发者通过短信通道向目标受众发送推广信息,包括促销活动、产品介绍、特价优惠等。短信推广API通常支持群发功能、链接跟踪、定时发送等特性,以满足企业对于推广活动的灵活需求&#xff0c…

VScode如何进行调试

参考资料: VS Code入门教程2020 #24 介绍launch.json launch.json配置文件 {// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid830387"version": "0.2.0&qu…

Python中的私有属性和方法

在Python编程中,封装是一种重要的面向对象特性,用于限制对类内部数据和方法的访问,确保对象的内部状态只能通过特定的方法进行修改。这种封装机制通过使用私有属性和方法来实现。本文将详细介绍Python中的私有属性和方法及其实现方式。 什么…

搜维尔科技:【研究】动作捕捉加速游戏开发行业的发展

动作捕捉加速游戏开发行业的发展 Sunjata 的故事始于 2004 年,它将席卷乌干达视频游戏行业,然后席卷全世界。但首先,Klan Of The Kings 的小团队需要工具来实现他们的愿景。 漫画家兼非洲民间传说爱好者罗纳德卡伊马 (Ronald Kayima) 在将…

idea navigate mysql生成实体类

参考:https://blog.51cto.com/u_16175427/7251120 使用idea导航MySQL生成实体类 1、在IDEA的顶部菜单中选择View -> Tool Windows -> Database 2、找到表,右键表Scripted Extensions -> Generate POJO...

定个小目标之刷LeetCode热题(45)

32. 最长有效括号 给你一个只包含 ( 和 ) 的字符串,找出最长有效(格式正确且连续)括号 子串的长度。 示例 1: 输入:s "(()" 输出:2 解释:最长有效括号子串是 "()"有事…

6. dolphinscheduler-3.0.0伪集群部署

环境说明: 主机名:cmc01为例 操作系统:centos7 安装部署软件版本部署方式centos7zookeeperzookeeper-3.4.10伪分布式hadoophadoop-3.1.3伪分布式hivehive-3.1.3-bin伪分布式clickhouse21.11.10.1-2单节点多实例dolphinscheduler3.0.0单节…

ELK kibana查询与过滤

ELK kibana查询与过滤 1、通过布尔操作符 AND 、 OR 和 NOT 来指定更多的搜索条件(注意:这AND、OR、NOT必须大写)。例如,搜索message包含服务层关键词并且日志级别为INFO的条目,您可以输入 message:“服务层” AND level:“INFO”。 2、要搜…

Qt 实战(7)元对象系统 | 7.4、属性系统:深度解析与应用

文章目录 一、属性系统:深度解析与应用1、定义属性2、属性系统的作用3、属性系统工作原理(1)Q_PROPERTY宏(2)moc 的作用(3)属性在元对象中的注册 4、获取与设置属性4.1、QObject::property()与Q…