阿里架构师详解缓存架构:如何减小没必要要的计算?

本文已收录GitHub,更有互联网大厂面试真题,面试攻略,高效学习资料等git

互联网应用的主要挑战就是在高并发状况下,大量的用户请求到达应用系统服务器,形成了巨大的计算压力。互联网应用的核心解决思路就是采用分布式架构,提供更多的服务器,从而提供更多的计算资源,以应对高并发带来的计算压力及资源消耗。github

那么有没有办法减小到达服务器的并发请求压力呢?或者请求到达服务器后,有没有办法减小没必要要的计算,下降服务器的计算资源消耗,尽快返回计算结果给用户呢?面试

有,解决的核心就是缓存。算法

所谓缓存,就是将须要屡次读取的数据暂存起来,这样在后面,应用程序须要屡次读取的时候,就没必要从数据源重复加载数据了,这样就能够下降数据源的计算负载压力,提升数据响应速度。数据库

通常说来,缓存能够分红两种,通读缓存和旁路缓存。浏览器

通读(read-through)缓存,应用程序访问通读缓存获取数据的时候,若是通读缓存有应用程序须要的数据,那么就返回这个数据;若是没有,那么通读缓存就本身负责访问数据源,从数据源获取数据返回给应用程序,并将这个数据缓存在本身的缓存中。这样,下次应用程序须要数据的时候,就能够经过通读缓存直接得到数据了。缓存

通读缓存在架构中的位置与做用以下图:服务器

阿里架构师详解缓存架构:如何减小没必要要的计算?

旁路(cache-aside)缓存,应用程序访问旁路缓存获取数据的时候,若是旁路缓存中有应用程序须要的数据,那么就返回这个数据;若是没有,就返回空(null)。应用程序须要本身从数据源读取数据,而后将这个数据写入到旁路缓存中。这样,下次应用程序须要数据的时候,就能够经过旁路缓存直接得到数据了。网络

旁路缓存在架构中位置与做用以下图:数据结构

阿里架构师详解缓存架构:如何减小没必要要的计算?

通读缓存

互联网应用中主要使用的通读缓存是 CDN 和反向代理缓存。

CDN(Content Delivery Network)即内容分发网络。咱们上网的时候,App 或者浏览器想要链接到互联网应用的服务器,须要网络服务商,好比移动、电信这样的服务商为咱们提供网络服务,创建网络链接才能够上网。

而这些服务商须要在全国范围内部署骨干网络、交换机机房才能完成网络链接服务,这些交换机机房可能会离用户很是近,那么互联网应用能不能在这些交换机机房中部署缓存缓存服务器呢?这样,用户就能够近距离得到本身须要的数据,既提升了响应速度,又节约了网络带宽和服务器资源。

固然能够。这个部署在网络服务商机房中的缓存就是 CDN,由于距离用户很是近,又被称做网络链接的第一跳。目前不少互联网应用大约 80% 以上的网络流量都是经过 CDN 返回的。

阿里架构师详解缓存架构:如何减小没必要要的计算?

CDN 只能缓存静态数据内容,好比图片、CSS、JS、HTML 等内容。而动态的内容,好比订单查询、商品搜索结果等必需要应用服务器进行计算处理后才能得到。所以,互联网应用的静态内容和动态内容须要进行分离,静态内容和动态内容部署在不一样的服务器集群上,使用不一样的二级域名,即所谓的动静分离,一方面便于运维管理,另外一方面也便于 CDN 进行缓存,使 CDN 只缓存静态内容。

反向代理缓存也是一种通读缓存。咱们上网的时候,有时候须要经过代理上网,这个代理是代理咱们的客户端上网设备。而反向代理则代理服务器,是应用程序服务器的门户,全部的网络请求都须要经过反向代理才能到达应用程序服务器。既然全部的请求都须要经过反向代理才能到达应用服务器,那么在这里加一个缓存,尽快将数据返回给用户,而不是发送给应用服务器,这就是反向代理缓存。

阿里架构师详解缓存架构:如何减小没必要要的计算?

用户请求到达反向代理缓存服务器,反向代理检查本地是否有须要的数据,若是有就直接返回,若是没有,就请求应用服务器,获得须要的数据后缓存在本地,而后返回给用户。

旁路缓存

CDN 和反向代理缓存一般会做为系统架构的一部分,不少时候对应用程序是透明的。而应用程序在代码中主要使用的是对象缓存,对象缓存是一种旁路缓存

不论是通读缓存仍是旁路缓存,缓存一般都是以 <key, value> 的方式存储在缓存中,好比,CDN 和反向代理缓存中,每一个 URL 是一个 key,那么 URL 对应的文件内容就是value。而对象缓存中,key 一般是一个 ID,好比用户 ID,商品 ID 等等,而 value 则是一个对象,就是 ID 对应的用户对象或者商品对象。

对于 <key, value> 的数据格式,咱们在前面在数据结构讨论过,比较快速的存取方式是使用 Hash 表。所以通读缓存和旁路缓存在实现上,基本上用的是 Hash 表

程序中使用的对象缓存,能够分红两种。一种是本地缓存,缓存和应用程序在同一个进程中启动,使用程序的堆空间存放缓存数据。本地缓存的响应速度快,可是缓存可使用的内存空间相对比较小,可是对于大型互联网应用所须要缓存的数据通以 T 计,这时候就要使用远程的分布式缓存了。

分布式缓存是指将一组服务器构成一个缓存集群,共同对外提供缓存服务,那么应用程序在每次读写缓存的时候,如何知道要访问缓存集群中的哪台服务器呢?咱们以 Memcached为例,看看分布式缓存的架构:

阿里架构师详解缓存架构:如何减小没必要要的计算?

Memcached 将多台服务器构成一个缓存集群,缓存数据存储在每台服务器的内存中。事实上,使用缓存的应用程序服务器一般也是以集群方式部署的,每一个程序须要依赖一个Memcached 的客户端 SDK,经过 SDK 的 API 访问 Memcached 的服务器。

应用程序调用 API,API 调用 SDK 的路由算法,路由算法根据缓存的 key 值,计算这个key 应该访问哪台 Memcached 服务器,计算获得服务器的 IP 地址和端口号后,API 再调用 SDK 的通讯模块,将 <key, value> 值以及缓存操做命令发送给具体的某台Memcached 服务器,由这台服务器完成缓存操做。

那么,路由算法又是如何计算获得 Memcached 的服务器 IP 端口呢?比较简单的一种方法,和 Hash 算法同样,利用 key 的 Hash 值对服务器列表长度取模,根据余数就能够肯定服务器列表的下标,进而获得服务器的 IP 和端口。

缓存注意事项

使用缓存能够减小没必要要的计算,可以带来三个方面的好处:

  1. 缓存的数据一般存储在内存中,距离使用数据的应用也更近一点,所以相比从硬盘上获取,或者从远程网络上获取,它获取数据的速度更快一点,响应时间更快,性能表现更好。
  2. 缓存的数据一般是计算结果数据,好比对象缓存中,一般存放通过计算加工的结果对象,若是缓存不命中,那么就须要从数据库中获取原始数据,而后进行计算加工才能获得结果对象,所以使用缓存能够减小 CPU 的计算消耗,节省计算资源,一样也加快了处理的速度。
  3. 经过对象缓存获取数据,能够下降数据库的负载压力;经过 CDN、反向代理等通读缓存获取数据,能够下降服务器的负载压力。这些被释放出来的计算资源,能够提供给其余更有须要的计算场景,好比写数据的场景,间接提升整个系统的处理能力。

可是缓存也不是万能的,若是不恰当地使用缓存,也可能会带来问题。

首先就是数据脏读的问题,缓存的数据来自数据源,若是数据源中的数据被修改了,那么缓存中的数据就变成脏数据了。

主要解决办法有两个,一个是过时失效,每次写入缓存中的数据都标记其失效时间,在读取缓存的时候,检查数据是否已通过期失效,若是失效,就从新从数据源获取数据。缓存失效依然可能会在未失效时间内读到脏数据,可是通常的应用均可以容忍较短期的数据不一致,好比淘宝卖家更新了商品信息,那么几分钟数据没有更新到缓存,买家看到的仍是旧数据,这种状况一般是能够接受的,这时候,就能够设置缓存失效时间为几分钟。

另外一个办法就是失效通知,应用程序更新数据源的数据,同时发送通知,将该数据从缓存中清除。失效通知看起来数据更新更加及时,可是实践中,更多使用的仍是过时失效。

此外,并非全部数据使用缓存都有意义。在互联网应用中,大多数数据访问都是有热点的,好比热门微博会被更多阅读,热门商品会被更多浏览。那么将这些热门的数据保存在缓存中是有意义的,由于缓存一般使用内存,存储空间比较有限,只能存储有限的数据,热门数据存储在缓存中,能够被更屡次地读取,缓存效率也比较高。

相反,若是缓存的数据没有热点,写入缓存的数据很难被重复读取,那么使用缓存就不是颇有必要了

总结

缓存是优化软件性能的杀手锏,任何须要查询数据、请求数据的场合均可以考虑使用缓存。缓存几乎是无处不在的,程序代码中可使用缓存,网络架构中可使用缓存,CPU、操做系统、虚拟机也大量使用缓存,事实上,缓存最先就是在 CPU 中使用的。对于一个典型的互联网应用而言,使用缓存能够解决绝大部分的性能问题,若是须要优化软件性能,那么能够优先考虑哪里可使用缓存改善性能。

除了本文提到的系统架构缓存外,客户端也可使用缓存,在 App 或者浏览器中缓存数据,甚至都不须要消耗网络带宽资源,也不会消耗 CDN、反向代理的内存资源,更不会消耗服务器的计算资源。

相关文章
相关标签/搜索