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

news2024/12/24 2:48:50

前言

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

        其实,缓存的应用远远不止存在于服务层(传统的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.coloradmin.cn/o/1937117.html

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

相关文章

AIGC Kolors可图IP-Adapter-Plus风格参考模型使用案例

参考: https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-Plus 代码环境安装: git clone https://github.com/Kwai-Kolors/Kolors cd Kolors conda create --name kolors python=3.8 conda activate kolors pip install -r requirements.txt python3 setup.py install…

2-39 基于matlab的二维拉普拉斯方程求解

基于matlab的二维拉普拉斯方程求解,用有限差分法求解二维拉普拉斯方程 所用的数值方案是空间二阶中心差分法 (5 点差分)。输出三维求解结果。程序已调通,可直接运行。 2-39 matlab 二维拉普拉斯方程求解 - 小红书 (xiaohongshu.com)

PDF-Extract-Kit (PDF内容抽取开源项目)

Github 地址:https://github.com/opendatalab/PDF-Extract-Kit 整体介绍 PDF文档中包含大量知识信息,例如文本、表格、图像、公式等。此外,PDF的文档布局也相当复杂,页眉、页脚、表格标题、图片标题等等,提取高质量的…

Nature子刊 | ATAC-seq、RNA-seq和蛋白组联合分析揭示脂质激活转录因子PPARα在肾脏代偿性肥大的作用机制

2023年6月,美国国立心肺血液研究所的研究团队在Nature Communications上发表题为“Signaling mechanisms in renal compensatory hypertrophy revealed by multi-omics”的文章,该研究通过在单侧肾切除的小鼠模型中使用多组学方法(蛋白质组学…

十一、面向对象进阶

文章目录 学习目标一、类方法和静态方法二、单例模式三、Python的继承3.1 继承的基本使用3.2 Python继承的特点3.3 私有属性的继承特点3.4 新式类和经典类四、对象相关的运算符和内置函数五、多态的使用5.1 子类重写父类的方法5.2 多态的使用学习目标 说出类方法和实例方法的区…

Spring中的IOC详解

文章目录 IOCIOC容器的工作原理Bean的生命周期Bean的自动装配AutowiredResourceInject 使用Spring底层组件 IOC Spring的核心之一是IOC,IOC全称为Inversion of Control,中文译为控制反转,是面向对象编程中的一种设计原则,可以用来…

栈和队列(一) ------基本概念,循环队列

目录 栈 python实现 队列 python实现 循环队列 力扣622- --循环队列 力扣20 ----有效括号判断 分析 代码 栈 python实现 在Python中实现一个栈(Stack)可以通过使用列表(list)来完成,因为列表提供了动态数…

【网络安全的神秘世界】Error:Archives directory /var/cache/apt/archives/partial is missing.

🌝博客主页:泥菩萨 💖专栏:Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 ✨问题描述 在kali中想要安装beef-xss软件包时,发生如下报错: Error: Archives directory /var/cac…

d3d12.dll 文件缺失如何解决?五种修复丢失问题的方法

d3d12.dll 文件缺失如何解决?它为什么会不见呢?今天,我们将探讨 d3d12.dll 文件的重要性、原因以及丢失时的解决策略。本文将全面介绍 d3d12.dll 文件,并提供五种修复丢失问题的方法。 d3d12.dll文件是什么的详细介绍 d3d12.dll …

【RAGFlow】Ubuntu系统下实现源码启动RAGFlow

一、RAGFlow 是什么? RAGFlow 是一款基于深度文档理解构建的开源 RAG(Retrieval-Augmented Generation)引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程,结合大语言模型(LLM)针对用…

【C语言初阶】C语言数组基础:从定义到遍历的全面指南

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C语言 “ 登神长阶 ” 🤡往期回顾🤡:C语言函数 🌹🌹期待您的关注 🌹🌹 ❀数组 📒1. 什么是数组…

【医学影像】X86+FPGA:支持AI医学影像设备应用的工控主板,赋能CT、MRI、X线、超声等医学影像设备

支持AI医学影像设备应用的工控主板 在我国人口老龄化问题不断加剧,对影像诊断需求持续增长,和国家利好高端医学影像市场发展的系列法规和政策接连出台的大环境下,AI医学影像设备产业迎来发展黄金期。紧跟发展大势,基于12/13代 In…

天途无人机林业应用解决方案

林业应用现状分析 森林环境较为复杂,人员无法快速到达现场,工作人员通常会面临监控盲区,林区爬山涉水困难多;森林防火重要性不可忽视,2019年全国共发生森林火灾2345起,森林防火仍为重中之重;环…

SAPUI5基础知识16 - 深入理解MVC架构

1. 背景 经过一系列的练习,相信大家对于SAPUI5的应用程序已经有了直观的认识,我们在练习中介绍了视图、控制器、模型的概念和用法。在本篇博客中,让我们回顾总结下这些知识点,更深入地理解SAPUI5的MVC架构。 首先,让…

Jvm基础(一)

目录 JVM是什么运行时数据区域线程私有1.程序计数器2.虚拟机栈3.本地方法栈 线程共享1.方法区2.堆 二、对象创建1.给对象分配空间(1)指针碰撞(2)空闲列表 2.对象的内存布局对象的组成Mark Word类型指针实例数据:对齐填充 对象的访问定位句柄法 三、垃圾收集器和内存…

React基础学习-Day04

React基础学习-Day04 常见的钩子函数及基础使用方式 1.useState useState 是 React 的一个 Hook,用于在函数组件中添加状态。它返回一个状态变量和一个更新该状态的函数。与类组件的 this.state 和 this.setState 相对应,useState 让函数组件也能拥有…

【第10章】Spring Cloud之Nacos动态配置

文章目录 前言一、上下文1. 新增配置2. 启动类3. 效果 二、注解 ( 推荐 ) \color{#00FF00}{(推荐)} (推荐)1. 获取配置2. 测试2.1 未配置2.2 配置值2.3 修改值 总结 前言 这一章我们通过两个案例来学习Nacos动态配置,通过在控制台修改服务端配置文件值,…

linux环境交叉编译openssl库,以使Qt支持https

一.前言 Qt若需要支持https,则需要openssl的支撑,并且要注意,Qt不同版本会指定对应的openssl版本库,比方我用的Qt5.15.10他要求用的openssl版本是1.1.1,你就不能用其他版本,不然基本就是失败报错。 如何查看Qt对应ope…

TK秘籍:深度剖析机房IP与住宅IP的利与弊

大家好,今天我们来聊聊TikTok运营中的一个重要环节——IP地址的选择。 想象一下,你在TikTok上发布视频,就像是在一个热闹的市集上摆摊,而IP地址就是你的摊位位置。选对了位置,你的摊位就能吸引更多顾客,也…

浪漫情怀:红酒中的诗意与情感

在生活的点滴细节中,总有些元素能触动我们内心较柔软的地方,唤起那份深深的浪漫情怀。而红酒,便是这其中的一种神奇媒介。它以其不同的色泽、香气和口感,让人沉醉其中,感受那份诗意与情感的交织。今天,就让…