一谈缓存,内心顿时豁然开朗。迫于key-value的形式,总感觉轻风拂面,杨柳依依,一切都尽在我掌握之中。犹如那一眼相中佳人的冲动,脑子里尽是佳人的容颜。
那缓存如果站在网站架构的角度,你知道它的设计原理和影响作用吗?
在商业的世界里,常说的一句话是 "现金为王"。在互联网、移动互联网乃至整个软件技术的世界里面,与之相近的就是 "缓存为王"。
为何这么说呢?
试想一下,你个完整的网络请求(HTTP、SOAP、RPC等),如果在执行过程的某个部分尚有缓存,是不是就能提前响应给客户端呢?
为何现在很多中大型公司在面试时,对缓存的应用、原理、高可用等一系列问题,都一扑啦的扔给你,让你难以招架。原因都在这。
缓存:存储在计算机上的一个原始数据复制,以便于访问
--维基百科
缓存是系统快速响应中的一种关键技术,是一组被保存起来以备将来使用的东西。介于应用开发和系统开发之间,是产品经理经常顾及不到的地方,也是技术架构设计中的非功能性约束。
应用开发我知道,这系统缓存是个什么情况呀,小吒哥?
不要着急,往后面看
顾名思义,由多个维度共同组成的缓存工程。因为缓存在不同的场景有着不同的意义。采用的技术手段也不同。
按缓存存在形式分:
操作系统是管理计算机硬件与软件资源的计算机程序,而硬、软件件运行速度的快慢基本由缓存决定,缓存的容量越大,相应的硬件运行速度也就越快。所以系统缓存就是操作系统调用硬件资源(内存、文件等)和调用应用程序时,能够启动加速执行的作用。
总结:操作系统存调用涉及到有缓存的部分,都可算系统缓存
软件运行都需建立在操作系统之上,在运行时需要把程序装载到内存中,但软件执行操作内存时都是基于虚拟内存映射的机制,并不是直接操作物理内存。虚拟内存以块表(内存块组成的表格)的形式来存储相关资源。
注:物理内存组成上由多个方块状的元素构成,该元素是内存管理的最小单位。每一个元素有8个小电容,存储8个bit,即1字节。
是不是和磁盘块差不多, ^_^ 。吒吒辉懂你呀
为提高系统的存取速度,在 地址映射机制中增加了一个小容量的联想寄存器,即块表。
内存块表
它 用来存放当前访问最频繁的少数活动页面的页数。当某用户需存取数据时,根据数据所在的逻辑页号在块表中找到对应的内存块号,再联系其页内地址,形成物理地址。
总结:读取数据时-->先找逻辑页--->排查内存块号--->获得物理层内存表示页内地址---->物理地址
如果块表中没有相应的逻辑页号,则地址映射仍然可以通过内存中的页表进行操作,只是它得到是空闲块号,必须将该块号填入块表中的空闲区。如果块表中没有空闲区,则根据淘汰算法淘汰块表中的某一行,再填入新的页号和块号。
我记得计算机获取缓存是按照就近原则的,那它们的优先级呢?
缓存会根据存储速度来选择最合适的存储器,离CPU越近的存储器,速度越快,每字节的成本越高,同时容量也因此越小
分层如下:寄存器(离CPU最近,寄存器速度最快)、高速缓存(缓存也是分级,有L1,L2等缓存)、主存(普通内存)、本地磁盘
日常开发常使缓存软件,根据软件系统所处位置不同,可分
多级缓存就类似金字塔模式。从上到下依次递减。类似于一个漏斗来过滤流量请求。如果绝大多数请求在客户端和网络交互的部分就抵消,那后端服务的压力就会大大减少。
根本在于为网站提供高性能服务,让用户具有更好的用户体验。以较少的成本获取更大的性能空间。
用户体验这个词最早被广泛认知是在20世纪90年代中期,由用户体验设计师唐纳德·诺曼(Donald Norman)提出和推广。
因信息技术在移动和图像处理等方面取得的进展已经使得人机交互(HCI)技术几乎渗透到人类活动的所有领域。这导致系统的评价指标从单纯的可用性,扩展到用户体验。
用户体验在人机交互技术发展过程中受到了相当的重视,其关注度与 传统的三大可用性指标(即效率、效益和基本主观满意度不相上下,甚至在某些方面更为重要。
ISO 9241-210 标准将用户体验定义为 “人们对正在使用或期望使用的产品、系统或者服务的认知印象和回应” 。因此,用户体验是主观的,且注重实际应用。
用户体验:即用户在使用一个产品或系统之前、使用期间和使用之后的全部感受,包括情感、信仰、喜好、认知印象、生理反应、心理反应、行为和成就等各个方面。
ISO标准也暗示了可用性也可以作为用户体验的一个方面,“可用性标准可以用来评估用户体验的一些方面”。不过,该ISO标准并没有进一步阐述用户体验和系统可用性之间的具体关系。显然,这两者是相互重叠的概念。
也许这就是产品不断折腾咱技术的原因,多少得懂点。不知你家产品如何?有无da人的冲动
影响用户体验的三因素:
系统性能是软件产品自身对用户体验最关键的因素。 因感受软件性能的主体是人,不同的人对于同样的软件可能有不同的主观感受,而且对于软件性能关心的视角也不同。
系统性能是一种非功能特性,它关注的不是某种特定的功能,而是在完成该功能时所展示出的及时性。
系统性能的指标一般包括 响应时间、延迟时间、吞吐量,并发用户数和资源利用率 等几方面。
响应时间是指系统对用户请求做出响应的时间,与人对软件性能的主观感受是一致的,完整地记录了整个系统处理请求的时间。
一般响应时间根据不同项目中的业务场景都会有确切的值,例:一个请求需保证在100ms、200ms以内。
你家首页响应需多少时间?
由于一个系统通常会提供许多功能,而不同功能的处理逻辑也千差万别,因而不同功能的响应时间也不尽相同,甚至同一功能在不同输入数据的情况下,响应时间也不相同。
所以,我们常说的响应时间通常指该软件系统 所有功能的平均响应时间 或者 所有功能中的最大响应时间。
有时候也需要对 每个或每组功能讨论其平均响应时间和最大响应时间。
在讨论软件性能时,我们更关心所开发软件自身的 “响应时间”。
比如:php响应时间就是从接受到Nginx请求后,并完成业务处理然后响应给nginx所消耗的时间。而用户就看发送请求到看到页面所需的时间
前者是整个软件自身的响应,后者是用户请求响应的时间。观看角度不同
就这样,我们可以把 用户感受到的响应时间 划分为 呈现时间和系统响应时间,
为啥你的请求就慢呢?
还可以把“系统响应时间”进一步分解为网络传输时间和“应用延迟时间”,
以后谈优化,那就应该从整个请求链路里着手,针对呈现、网络传输、应用处理时间
吞吐量 是指系统在单位时间内处理请求的数量。
单位时间是项目自身规划响应时间来进行描述的,但常用 1s 来衡量处理成功的请求量。
那我网站的吞吐量怎么计算呢? 作为小吒的我,还是补了课的
要计算吞吐量首先要看你的时间换算和流量情况。
注:
用户不会全天都使用软件,一般晚上不会使用或者使用人很少。但也分业务,你像直播、外卖等。 但一天12小时基本上满足一个用户当天最大使用软件的时间。
具体用户在线使用App的时间也不确定,具体应该根据自身项目统计用户的使用时间和总流量来计算平均QPS。利用高峰期流量来计算最大QPS。
对于无并发的应用系统而言,吞吐量与响应时间成严格的反比关系,实际上此时吞吐量就是响应时间的倒数。
无并发的应用都是单机应用,对于互联网或者移动互联网上的产品而言。
并发用户数是指系统可以同时承载的正常使用系统功能的用户数量,值越大,那处理能力就越强。
资源利用率反映的是在一段时间内资源平均被占用的情况。
从浏览器--->网络--->应用服务器--->数据库,通过在各个层面应用缓存技术,将大幅提高提升整个系统的性能。
例如:缓存离客户端更近,从缓存请求内容比从源服务器所用时间更少,呈现速度更快,系统就显得更灵敏。缓存数据的重复使用,大大降低了用户的带宽使用,其实也是一种变相的省钱(如果流量要付费的话),同时保证了带宽请求在一个低水平上,更容易维护。
所以,使用 缓存技术,可以降低系统的响应时间,减少网络传输时间和应用延迟时间,进而提高了系统的吞吐量,增加了系统的并发用户数
利用缓存还可以最小化系统的工作量,使用了缓存就可以不必反复从数据源中查找,缓存所创建或提供的同一条数据更好地利用了系统的资源。
因此,缓存是系统调优时常用且行之有效的手段,无论是操作系统还是应用系统,缓存策略无处不在。“缓存为王”本质上是系统性能为王,对用户而言就是用户体验为王。
最初的网站可能就是一台物理主机,放在IDC或者租用的是云服务器,上面只运行着 应用服务器和数据库,LAMP(linux Apache MySQL PHP)就是这样流行起来的。
单体架构
由于网站具备一定的特色,吸引了部分用户的访问,逐渐会发现系统的压力越来越大,响应速度越来越慢,而这个时候比较明显的往往是 数据库与应用 的互相影响,于是将应用服务器和数据库服务器从物理上分离开来,变成了两台机器。让其不再互相影响,来支撑更高的流量。
随着访问网站的人数越来越多,响应速度又开始变慢了,可能是 访问数据库的操作太多,导致数据连接竞争激烈,因此缓存开始登场。
这里不难看出,数据库往往是考虑优化的首选,毕竟没缓存请求直连数据库,而数据库又是数据的集中地,查数据还会涉及磁盘I/O。压力可想而知
若想通过缓存机制来减少数据库连接资源的竞争和对数据库读的压力,那么可如下选择:
静态缓存更倾向于静态资源的缓存和浏览器的缓存。动态缓存是通过页面访问后,再编译生成缓存文件,提供给后续请求访问。有点像模板引擎
那我数据库写呢?
此刻流量上升,主要针对访问,写请求也会上升,但性能瓶颈在于数据库的读操作上。可以说写还构不成太大的威胁。如果写不够,那只能扩容多个实例来应对写的不足。
随着访问量的持续增加,系统又开始变慢,怎么办?
使用 数据缓存,将系统中重复获取的数据信息从数据库加载到本地,同时降低了数据库的负载。 随着系统访问量的再度增加,应用服务器又扛不住了,就开始增加Web服务器。
那如何保持应用服务器中数据缓存信息的同步呢?
例如: 之前缓存的用户数据等,这个时候通常会开始使用缓存同步机制以及共享文件系统或共享存储等。 在享受了一段时间的访问量高速增长后,系统再次变慢。
开始数据库调优,优化数据库自身的缓存,接下来是采用数据库集群以及分库分表的策略。
分库分表的规则是有些复杂的,考虑增加一个通用的框架来实现分库分表的数据访问,这个就是数据访问层(Data Access Layer,DAL)。
该阶段可能会发现之前的缓存同步方案会出现问题,因为数据量太大,导致现在不太可能将缓存存储在本地后再同步,这样会造成同步的时间延迟从而增大响应时间、数据不一致性,数据库耦合缓存。
于是分布式缓存终于来了,将大量的数据缓存转移到分布式缓存上。
如果使用共享文件系统或共享存储有啥问题?
纵观网站架构的发展历程,缓存技术往往就是解除烦恼的灵丹妙药,这再次证明了什么是缓存为王。
客户端缓存相对于其他端的缓存而言,要简单一些,通常是和服务端以及网络侧的应用或缓存配合使用。
在互联网应用中,根据应用区分有二大类。
页面缓存是将之前渲染的页面保存为静态文件,当用户再次访问时可以避开网络连接,从而减少负载,提升性能和用户体验。
随着单页面应用(Single Page Application,SPA)的广泛使用,加之html5支持了离线缓存和本地存储,大部分BS应用的页面缓存都可以举重若轻了。
在HTML5中使用本地缓存的方法,示例代码如下:
localStorage.setItem("mykey","吒吒辉")
localStorage.getItem("mykey","吒吒辉")
localStorage.removeItem("mykey")
localStorage.clear()
什么是单页面应用?
SPA是在 Web 设计上使用单一页面,利用 JAVAScript 操作 Dom 的技术实现各种应用。此模式下一个系统只加载一次资源,之后的操作交互、数据交互是通过路由、ajax来进行,页面并没有刷新。
常见的路由形式如:http:.//xxx/shell.htm1#page1。
一般在Vue下使用很明显。像商城活动页、登录页这种都是很好的SPA落地实践。
HTML5提供的离线应用缓存机制,使得网页应用可以离线使用(无网络也可访问),这种机制在浏览器上支持度非常广,可以放心地使用该特性来加速页面的访问。开启离线缓存的步骤如下:
注:manifest 文件需要配置正确的 MIME-type,即 "text/cache-manifest"。必须在 web 服务器上进行配置。
例:nginx要修改配置目录下的 mime.types 文件,添加manifest文件映射:
text/cache-manifest manifest;
目前html标签的manifest属性已废弃了,可移步 webpack。
页面缓存
由图可知:
注意:manifest文件中列出的资源URL必须和manifest本身使用同样的网络协议,详情可参考W3C相关的标准文档。
浏览器缓存是根据一套与服务器约定的规则进行工作的,工作规则很简单:检查以确保副本为最新,通常只要一次会话请求。
浏览器会在硬盘上专门开辟一个空间来存储资源副本作为缓存。
在用户触发 后退操作或点击 一个之前看过链接的时候,浏览器缓存会很管用。如果访问系统中的同一张图片,该图片可以从浏览器缓存中调出并几乎立即显现出来。
对浏览器而言,HTTP1.0提供了一些很基本的缓存特性,参数例如:
每次请求web容器时会先检查客户端缓存资源是否还有效,无效则会把上次服务端响应的最后修改时间作为 If-Modified-Since 的值发送给服务器进行判断,如果文件没有改变,服务器采用 304-Not Modified做响应头和一个为空的响应体。客户端收到304响应,就可以使用缓存的文件版本了。
为什么客户端无效,还要发送HTTP请求来判断文件是否修改呢?
如果缓存资源有效那就是直接来读取客户端缓存,不需要发送HTTP请求。但有一些情况就需注意,比如当用户按F5或者点击Refresh按钮的时候就算对于有Expires的URI,一样也会发一个HTTP请求出去,所以就需要通过Last-Modified来判断。
而且客户端和服务端的时间可能不一样,这样就会导致客户端已过期,但服务端并没有。如果有判断机制,那么响应就会减少响应体的数据发送。 或者客户端只是单纯的 Expires 过期,但资源并没改变,这时就需要检查文件是否有变化。
HTTP 1.1有了较大的增强,缓存系统被形式化了,引入了实体标签 **e-tag 和 Cache-Control。
以Web浏览器使用 e-tag 为例,如下图所示。
在配置了 Last-Modified/ETag 的情况下,浏览器再次访问统一URI的资源时,还是会发送请求到服务器询问文件是否已经修改,如果没有,服务器会生成304-Not Modified应答和一个空的响应体给浏览器,浏览器则直接从本地缓存取数据;如果数据有变化,就将整个数据重新发给浏览器。
Last-Modified/ETag 与 Cache-Control/Expires的作用是不一样的。前者是每次询问实体版本是否有变化,后者是直接检查本地的缓存还在有效的时间范围内,如果在就不会发送任何请求。
当本地副本根据 Cache-Control/Expires 判断是否还在有效期内,如果不在,则会再次发送请求去服务器询问修改时间(Last-Modified)或实体标识(e-tag)。
Cache-Control与Expires的功能一致,都是指当前资源的有效期,控制浏览器是直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires。
一般情况下,使用 Cache-Control/Expires 会配合 Last-Modified/ETag 一起使用,因为即使服务器设置缓存时间,当用户点击 刷新 按钮时,浏览器会忽略缓存继续向服务器发送请求,这时Last-Modified/ETag将能够很好利用服务端的返回码304,从而减少响应开销。
通过在HTML页面的节点中加入meta标签,可告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉取。代码如下:
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
只不过,只有部分浏览器才支持这一用法,而且一般 缓存代理服务器都不支持,因为代理本身不解析 HTML 的内容。 浏览器缓存能够极大地提升终端用户的体验,那么,用户在使用浏览器的时候,会有各种操作,如输入地址后回车、按F5刷新等等,这些行为对缓存的影响如下所示。
尽管混合编程(hybrid programming)已成为时尚,吵在尖端,但移动互联网目前还是原生应用(APP)的天下。无论大小型APP,灵活的缓存不仅大大减轻了服务器的压力,而且会因更快速的用户体验而方便用户。 如何把APP 缓存对于业务组件透明,以及APP缓存数据的及时更新,是APP缓存能否成功应用起来的关键。
组件透明是什么?
即组件对调用者来说没有什么影响,也不需要维护。开箱即用。
APP缓存可使用哪些缓存?
APP可以将内容缓存在 内存、文件或本地数据库(例如SQLite)中,但基于 内存的缓存 要谨慎使用。
APP使用数据库缓存的方法:
优点:方法具有灵活存放文件的属性,进而提供了很大的扩展性,可以为其他的功能提供良好的支持。
缺点:信息存储过多,存储容量会降低。所以要根据业务选择合适主要的信息存储
注意数据库缓存的清理机制 。
对APP中的某些界面,可以采用文件缓存的方法。这种方法使用文件操作的相关API得到文件的最后修改时间,与当前时间判断是否过期,从而实现缓存效果。
但需要注意的是,不同类型文件的缓存时间不一样。比如:
文件类型:
网络环境:
在IOS开发中,SDWebImage是一个很棒的图片缓存框架,主要类组成的结构如下所示。
SDWebImage 是个比较大的类库,提供一个UIImageView的类以支持远程加载来自网络的图片,具有缓存管理、异步下载、同一个URL下载次数控制和优化等特征。使用时,只需要在头文件中引入 #import"UIImageView+WebCache.h" 即可调用异步加载图片方法:
(void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options;
URL是图片的地址
默认情况下,SDWebImage会忽略Header中的缓存设置,将图片以URL为key进行保存,URL与图片是一个映射关系。在APP请求同一个URL时,SDWebImage会从缓存中取得图片。将第三个参数设置为SDWebImageRefreshCached就可以实现图片更新操作,例如:
NSURL *url = [NSURL URLWithString:@"http://www.zhazhahui.com/image.png"];
UIImage *defaultImage = [UIImage imageNamed:@"zhazhahui.png"];
[self.imageView setImageWithURL:url placeholderImage:defaultImage options:SDWebImageRefreshCached];
SDWebImage中有两种缓存
框架都提供了相应的清理方法:
[[[SDWebImageManager sharedManager] imageCache] clearDisk];
[[[SDWebImageManager sharedManager] imageCache] clearMemory];
要注意的是,在iOS7中,缓存机制做了修改,使用上述两个方法只清除了SDWebImage的缓存,没有清除系统的缓存,所以可以在清除缓存的代码中添加以下代码:
[[NSURLCache sharedURLCache] removeAllCachedResponses];
以上就是本文分享,但内容未完,这还是开胃小菜。若有帮助,欢迎关注、分享+收藏额
需获取本系列总结大纲直接后台私信回复“分布式缓存”即可免费领取!!!
其实,大纲很早就写好了,但对里面内容的呈现方式,细节原理就一直不知道如何撰写才能达到明朗的效果。
这文章花了我非常多时间。哎,菜就是菜,吒吒辉我也不找借口了。。。 吒就是吒
还是太弱
如果大家在阅读过程中,发现了不理解或有错误的地方,欢迎跟在底部留言,你们的每一条留言,吒吒辉都会回复。