深入缓存核心技术:大型网站多级缓存的分层架构

服务器收到请求,会把 If-Modified-Since 字段与服务器上保存的 Last-Modified 字段作比较:

  • 若服务器上的 Last-Modified 最后修改时间大于请求的 If-Modified-Since,说明资源被改动过,就会把资源(包括 Header+Body)重新返回给浏览器,同时返回状态码 200。

  • 若资源的最后修改时间小于或等于 If-Modified-Since,说明资源没有改动过,只会返回 Header,并且返回状态码 304。浏览器接受到这个消息就可以使用本地缓存库的数据。

注意:Last-Modified 和 If-Modified-Since 指的是同一个值,只是在客户端和服务器端的叫法不同。

1.2.2. ETag / If-None-Match 规则

客户端第一次请求的时候,服务器会给每个资源生成一个 ETag 标记。这个 ETag 是根据每个资源生成的唯一 Hash 串,资源如何发生变化 ETag 随之更改,之后将这个 ETag 返回给客户端,客户端把请求的资源和 ETag 都缓存到本地。

ETag 被保存以后,在下次请求时会当作 If-None-Match 字段被发送出去。

在浏览器第二次请求服务器相同资源时,会把资源对应的 ETag 一并发送给服务器。在请求时 ETag 转化成 If-None-Match,但其内容不变。

服务器收到请求后,会把 If-None-Match 与服务器上资源的 ETag 进行比较:

  • 如果不一致,说明资源被改动过,则返回资源(Header+Body),返回状态码 200。

  • 如果一致,说明资源没有被改过,则返回 Header,返回状态码 304。浏览器接受到这个消息就可以使用本地缓存库的数据。

注意:ETag 和 If-None-Match 指的是同一个值,只是在客户端和服务器端的叫法不同。

2. CDN 缓存

HTTP 缓存主要是对静态数据进行缓存,把从服务器拿到的数据缓存到客户端/浏览器。

如果在客户端和服务器之间再加上一层 CDN,可以让 CDN 为应用服务器提供缓存,如果在 CDN 上缓存,就不用再请求应用服务器了。并且 HTTP 缓存提到的两种策略同样可以在 CDN 服务器执行。

CDN 的全称是 Content Delivery Network,即内容分发网络。

让我们来看看它是如何工作的吧:

  • 客户端发送 URL 给 DNS 服务器。

  • DNS 通过域名解析,把请求指向 CDN 网络中的 DNS 负载均衡器。

  • DNS 负载均衡器将最近 CDN 节点的 IP 告诉 DNS,DNS 告之客户端最新 CDN 节点的 IP。

  • 客户端请求最近的 CDN 节点。

  • CDN 节点从应用服务器获取资源返回给客户端,同时将静态信息缓存。注意:客户端下次互动的对象就是 CDN 缓存了,CDN 可以和应用服务器同步缓存信息。

CDN 接受客户端的请求,它就是离客户端最近的服务器,它后面会链接多台服务器,起到了缓存和负载均衡的作用。

3. 负载均衡缓存

说完客户端(HTTP)缓存和 CDN 缓存,我们离应用服务越来越近了,在到达应用服务之前,请求还要经过负载均衡器。

虽说它的主要工作是对应用服务器进行负载均衡,但是它也可以作缓存。可以把一些修改频率不高的数据缓存在这里,例如:用户信息,配置信息。通过服务定期刷新这个缓存就行了。

以 Nginx 为例,我们看看它是如何工作的:

  • 用户请求在达到应用服务器之前,会先访问 Nginx 负载均衡器,如果发现有缓存信息,直接返回给用户。

  • 如果没有发现缓存信息,Nginx 回源到应用服务器获取信息。

  • 另外,有一个缓存更新服务,定期把应用服务器中相对稳定的信息更新到 Nginx 本地缓存中。

4. 进程内缓存

通过了客户端,CDN,负载均衡器,我们终于来到了应用服务器。应用服务器上部署着一个个应用,这些应用以进程的方式运行着,那么在进程中的缓存是怎样的呢?

进程内缓存又叫托管堆缓存,以APC为例,同时会受到托管堆回收算法的影响。

由于其运行在内存中,对数据的响应速度很快,通常我们会把热点数据放在这里。

在进程内缓存没有命中的时候,我们会去搜索进程外的缓存或者分布式缓存。这种缓存的好处是没有序列化和反序列化,是最快的缓存。缺点是缓存的空间不能太大,对垃圾回收器的性能有影响。

这里我们需要关注几个缓存的回收策略,具体的实现架构的回收策略会有所不同,但大致的思路都是一致的:

  • FIFO(First In First Out):先进先出算法,最先放入缓存的数据最先被移除。

  • LRU(Least Recently Used):最近最少使用算法,把最久没有使用过的数据移除缓存。

  • LFU(Least Frequently Used):最不常用算法,在一段时间内使用频率最小的数据被移除缓存。

在分布式架构的今天,多应用中如果采用进程内缓存会存在数据一致性的问题。

这里推荐两个方案:

  • 消息队列修改方案

  • Timer 修改方案

4.1. 消息队列修改方案

应用在修改完自身缓存数据和数据库数据之后,给消息队列发送数据变化通知,其他应用订阅了消息通知,在收到通知的时候修改缓存数据。

4.2. Timer 修改方案

为了避免耦合,降低复杂性,对“实时一致性”不敏感的情况下。每个应用都会启动一个 Timer,定时从数据库拉取最新的数据,更新缓存。

不过在有的应用更新数据库后,其他节点通过 Timer 获取数据之间,会读到脏数据。这里需要控制好 Timer 的频率,以及应用与对实时性要求不高的场景。

进程内缓存有哪些使用场景呢?

  • 场景一:只读数据,可以考虑在进程启动时加载到内存。当然,把数据加载到类似 Redis 这样的进程外缓存服务也能解决这类问题。

  • 场景二:高并发,可以考虑使用进程内缓存,例如:秒杀。

5. 分布式缓存

说完进程内缓存,自然就过度到进程外缓存了。与进程内缓存不同,进程外缓存在应用运行的进程之外,它拥有更大的缓存容量,并且可以部署到不同的物理节点,通常会用分布式缓存的方式实现。

分布式缓存是与应用分离的缓存服务,最大的特点是,自身是一个独立的应用/服务,与本地应用隔离,多个应用可直接共享一个或者多个缓存应用/服务。

既然是分布式缓存,缓存的数据会分布到不同的缓存节点上,每个缓存节点缓存的数据大小通常也是有限制的。

数据被缓存到不同的节点,为了能方便的访问这些节点,需要引入缓存代理,类似 Twemproxy。他会帮助请求找到对应的缓存节点。

同时如果缓存节点增加了,这个代理也会只能识别并且把新的缓存数据分片到新的节点,做横向的扩展。

为了提高缓存的可用性,会在原有的缓存节点上加入 Master/Slave 的设计。当缓存数据写入 Master 节点的时候,会同时同步一份到 Slave 节点。

一旦 Master 节点失效,可以通过代理直接切换到 Slave 节点,这时 Slave 节点就变成了 Master 节点,保证缓存的正常工作。

每个缓存节点还会提供缓存过期的机制,并且会把缓存内容定期以快照的方式保存到文件上,方便缓存崩溃之后启动预热加载。

5.1. 高性能

当缓存做成分布式的时候,数据会根据一定的规律分配到每个缓存应用/服务上。

如果我们把这些缓存应用/服务叫做缓存节点,每个节点一般都可以缓存一定容量的数据,例如:Redis 一个节点可以缓存 2G 的数据。

如果需要缓存的数据量比较大就需要扩展多个缓存节点来实现,这么多的缓存节点,客户端的请求不知道访问哪个节点怎么办?缓存的数据又如何放到这些节点上?

缓存代理服务已经帮我们解决这些问题了,例如:Twemproxy 不但可以帮助缓存路由,同时可以管理缓存节点。

这里有介绍三种缓存数据分片的算法,有了这些算法缓存代理就可以方便的找到分片的数据了。

5.1.1. 哈希算法

Hash 表是最常见的数据结构,实现方式是,对数据记录的关键值进行 Hash,然后再对需要分片的缓存节点个数进行取模得到的余数进行数据分配。

例如:有三条记录数据分别是 R1,R2,R3。他们的 ID 分别是 01,02,03,假设对这三个记录的 ID 作为关键值进行 Hash 算法之后的结果依旧是 01,02,03。

我们想把这三条数据放到三个缓存节点中,可以把这个结果分别对 3 这个数字取模得到余数,这个余数就是这三条记录分别放置的缓存节点。

Hash 算法是某种程度上的平均放置,策略比较简单,如果要增加缓存节点,对已经存在的数据会有较大的变动。

5.1.2. 一致性哈希算法

一致性 Hash 是将数据按照特征值映射到一个首尾相接的 Hash 环上,同时也将缓存节点映射到这个环上。

如果要缓存数据,通过数据的关键值(Key)在环上找到自己存放的位置。这些数据按照自身的 ID 取 Hash 之后得到的值按照顺序在环上排列。

如果这个时候要插入一条新的数据其 ID 是 115,那么就应该插入到如下图的位置。

同理如果要增加一个缓存节点 N4 150,也可以放到如下图的位置。

这种算法对于增加缓存数据,和缓存节点的开销相对比较小。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

面试资料整理汇总

成功从小公司跳槽进蚂蚁定级P7,只因刷了七遍这些面试真题

成功从小公司跳槽进蚂蚁定级P7,只因刷了七遍这些面试真题

这些面试题是我朋友进阿里前狂刷七遍以上的面试资料,由于面试文档很多,内容更多,没有办法一一为大家展示出来,所以只好为大家节选出来了一部分供大家参考。

面试的本质不是考试,而是告诉面试官你会做什么,所以,这些面试资料中提到的技术也是要学会的,不然稍微改动一下你就凉凉了

在这里祝大家能够拿到心仪的offer!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
资料整理汇总**

[外链图片转存中…(img-NykPnmKH-1713749312418)]

[外链图片转存中…(img-P7fU0HXO-1713749312419)]

这些面试题是我朋友进阿里前狂刷七遍以上的面试资料,由于面试文档很多,内容更多,没有办法一一为大家展示出来,所以只好为大家节选出来了一部分供大家参考。

面试的本质不是考试,而是告诉面试官你会做什么,所以,这些面试资料中提到的技术也是要学会的,不然稍微改动一下你就凉凉了

在这里祝大家能够拿到心仪的offer!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值