3.缓存

负载平衡可帮助在越来越多的服务器上横向扩展,但缓存将使您能够更好地利用已有的资源,并使一些无法实现的产品要求变得可行。缓存利用了一个特性:可能再次请求最近请求的数据。

缓存用于每个计算层:硬件,操作系统,Web浏览器,Web应用程序等。缓存就像短期记忆。

它具有有限的存储空间,但通常比原始数据源更快,并包含最近访问的项目。

APP SERVER CACHE

当用户请求一个数据的时候,先看缓存有没有,如果没有在去后台拿,拿到之后再存入缓存,那么下次来用的时候,就可以直接来缓存拿。

但是缓存在多个节点上,因为请求被随机分发。所以你同样的请求可能会分发到不同机器上,造成缓存MISS率的升高

我们需要通过全局CACHE 或者 分布式的CACHE来解决这个问题。

分布式CACHE

在分布式缓存中,其每个节点都拥有部分缓存数据。通常,使用一致的HASH函数来划分缓存,使得如果请求节点正在寻找某个数据片段,则它可以快速知道在分布式缓存中的哪个位置以确定该数据是否可用。在这种情况下,每个节点都有一小部分缓存。因此,分布式缓存的一个优点是我们可以轻松地增加缓存空间,这可以通过向请求池添加节点来实现。

分布式缓存的缺点是丢失的节点。一个节点一旦丢失,那么缓存在那个节点上的数据就会也跟着一起丢失。一些分布式缓存通过在不同节点上存储多个数据副本来解决这个问题。但是,您可以想象这种逻辑如何快速复杂化,尤其是在您从请求层添加或删除节点时。

试想如果不解决这个问题会怎么样。当一个节点挂了,虽然节点消失而且那部分分缓存丢失,但请求只会从原点拉出,所以原点可以分发给另一个SERVER去做这个请求,然后再重新缓存 - 所以它不一定是灾难性的!

全局CACHE

全局缓存是所有节点都使用相同的单个缓存空间。每个请求节点以与本地节点相同的方式查询缓存。这种缓存可能会变得有点复杂,因为随着客户端和请求数量的增加,很容易压倒单个缓存,但在某些体系结构中非常有效(特别是那些使用这种全局缓存非常快速的专用硬件,或者有一个需要缓存的固定数据集,就是数据集不会动态变化的)。

下图中描述了两种常见的全局缓存形式。首先,当在缓存中找不到缓存的响应时,缓存本身负责从底层存储中检索丢失的数据。第二个,请求节点负责检索缓存中找不到的任何数据。


image.png

大多数系统会选用第一种,因为这样对CLIENT来说更加简答,只要和缓存打交道取数据,可以不知道DB的存在。

但有些情况我们会需要第2种。
第一个情况,就是取的数据都是超大的文件,如果也用第一种直接缓存下来的话,会把很多之前缓存的小数据将给踢掉,因为缓存的大小是有限的。这样捡了芝麻丢了西瓜的做法,会极大增加缓存MISS率。

第二个情况,有些数据是希望被永久缓存的。这往往是APP 比 缓存层更加了解。所以也需要让APP去决定是直接访问DB,还是直接去拿缓存。

消息分发网络CDN

CDN是一种缓存,可用于服务大量静态媒体的站点。在典型的CDN设置中,请求将首先向CDN询问一块静态媒体资源;如果CDN在本地可用,它将提供该内容。如果它不可用,CDN将向后端服务器查询该文件,然后在本地缓存它并将其提供给请求用户。

如果我们正在构建的系统还不够大,无法拥有自己的CDN,我们可以通过使用像Nginx这样的轻量级HTTP服务器从单独的子域(例如static.yourservice.com)提供静态媒体来简化未来的转换,以及稍后将DNS从服务器切换到CDN。

缓存失效

虽然缓存非常棒,但它确实需要一些维护来保持缓存与真实来源(例如,数据库)的一致性。如果在数据库中修改了数据,则应在缓存中使其无效,否则会导致应用程序行为不一致。

解决此问题称为缓存失效,有三种主要方案:

直写缓存:在此方案下,数据同时写入缓存和相应的数据库。缓存的数据允许快速检索,并且由于相同的数据被写入永久存储器,我们将在缓存和存储之间具有完全的数据一致性。此外,该方案可确保在发生崩溃,电源故障或其他系统中断时不会丢失任何内容。

尽管写入可以最大限度地降低数据丢失的风险,但由于每次写入操作必须在将成功返回到客户端之前完成两次(一次是缓存的写成功,一次是DB的写成功),因此该方案的缺点是写入操作的延迟更高。

绕写缓存:此技术类似于通过缓存写入,但数据x先写入永久存储,随后更新到缓存。这可以减少高速缓存被写入操作淹没,这些写入操作随后不会被重新读取,但缺点是对最近写入的数据的读取请求将“缓存未命中”,并且必须从较慢的后端存储读取和更高的延迟。

回写缓存:在此方案下,数据仅被写入高速缓存,并立即向客户端确认完成。永久存储器的写入在一个异步的请求下完成。这导致写入密集型应用程序的低延迟和高吞吐量,但是,这种速度伴随着崩溃或其他不利事件导致数据丢失的风险,因为写入数据的唯一副本在缓存中。

CACHE 淘汰策略

先进先出(FIFO):缓存首先淘汰最先进来的第一个块,而不考虑之前访问的频率或次数。

后进先出(LIFO):高速缓存首先驱逐最后进来的块,而不考虑之前访问的频率或次数。

最近最少使用(LRU):首先丢弃最近最少使用的项目。

最近使用的(MRU):丢弃与LRU相比,淘汰最多使用的项目。

最少使用(LFU):计算数据访问的频率。最少使用的那些首先被丢弃。

随机替换(RR):随机选择候选项并在必要时丢弃它以腾出空间。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容