type
status
date
slug
summary
tags
category
icon
password
Property
Nov 8, 2022 06:33 AM
互联网应用缓存可以分成两种,通读缓存和旁路缓存。
通读(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 和端口。
缓存:通过从缓存读取数据,加快响应时间,减少后端计算压力,缓存主要是提升读的性能。
消息队列:通过将数据写入消息队列,异步进行计算处理,提升系统的响应时间和处理速度,消息队列主要是提升写的性能。
集群:将单一服务器进行伸缩,构建成一个集群完成同一种计算任务,从而提高系统在高并发压力时候的性能。各种服务器都可以构建集群,应用集群、缓存集群、数据库集群等等。