当前位置: 首页 > 科技观察

分布式系统缓存架构深入讲解

时间:2023-03-14 23:37:18 科技观察

大家好,我是“老黑”。缓存已经是一个老掉牙的技术了。在高并发阅读的情况下,对于阅读服务来说可以说是对抗流量的灵丹妙药。高并发的三大利器:缓存、限流、降级。今天我们就来聊聊缓存。“对于缓存,我的理解是让数据离用户更近,目的是让用户的访问速度更快。”所以离用户缓存越近,越快越有效!缓存的工作原理是先从缓存中获取数据,如果有数据就直接返回给用户,如果没有数据就从慢速设备中读取实际数据放入缓存。按照层级关系来划分缓存,这也是我们今天的“提纲”:浏览器缓存浏览器是我们上网的重要工具。为了让我们能顺利上网,它还会帮我们缓存一些东西,主要是存放一些实时的不太敏感的数据,比如商品详情页框、商家评分、评论、广告词等,不能使用浏览器缓存对于实时性要求高的数据。浏览器缓存是有过期时间的,我们可以通过响应头Expires和Cache-control来控制。ClientcacheClientcache很容易理解,就是存放在客户端的缓存。它的使用场景并不多。我们在大促的时候,为了避免服务器被瞬时流量压垮,我们通常会在大促到来之前,发送一些app需要访问的素材(比如js/css/image等)。去客户端做缓存,大促来的时候app不需要拉这些素材。此外,一些口袋数据或样式文件也会存储在客户端缓存中,以确保在服务器异常或网络异常时应用不会崩溃。CDN缓存CDN(ContentDeliveryNetwork),即内容分发网络。它建立并覆盖在承载网上,是由分布在不同区域的边缘节点服务器群组成的分布式网络。我们通常会在CDN缓存中存储一??些静态页面数据、活动页面、图片等数据。CDN缓存有两种机制:推送机制(内容变化后主动推送数据到CDN节点)和拉取机制(先访问CDN节点,没有数据时,会从源头获取数据)服务器返回给CDN节点存储)。比如你要买车,就应该去4S店买车。如果4S店有,可以直接拿走。如果4S店没有,那么4S店需要进一批货,然后返店,再给你。在这种情况下,4S店实际上承担了CDN缓存节点的角色。反向代理缓存反向代理,我们一般指的是反向代理服务器Nginx。Nginx缓存主要分为NginxHttp缓存和Nginx代理层缓存。NginxHttp缓存提供了expires、etag、if-modified-since指令来实现反向代理缓存。Nginx代理层缓存主要配置了Http模块和proxy_cacahe模块。本地缓存本地缓存一般是指在客户端的本地物理内存中划分出一部分来缓冲客户端写回服务器的数据。从全局来看,我们可以有“磁盘缓存”、“CPU缓存”、“应用缓存”。“磁盘缓存”分为读缓存和写缓存。读缓存是指操作系统在内存比较空闲时,将已经读取过的文件数据保存在内存空间中(这段内存空间称为“内存池”),当下次软件或用户读取同一个文件时,不要'必须从磁盘重新读取,这会提高速度。WriteCache实际上是将要写入磁盘的数据保存在系统为WriteCache分配的内存空间中,当内存池中保存的数据达到一定程度时,再将数据保存到硬盘中。“CPU缓存”可分为一级缓存(L1Cache)、二级缓存和三级缓存(L2/L3)。当CPU要读取一段数据时,先从L1中查找,如果没有,再从L2/L3中查找,如果没有,再从内存中查找,如果内存中没有,然后从磁盘中查找。查找顺序为:CPU->L1->L2/L3->Memory->Disk。“应用缓存”分为本地应用缓存和其他应用缓存。本地应用程序缓存是指该服务使用的缓存。以Java服务为例,分为堆内缓存和堆外缓存。堆内缓存一般是指Java堆的缓存对象。堆内缓存的优点是不需要序列化/反序列化,也是最快的缓存。缺点也很明显。当缓存的数据很多时,GC(garbagecollection))的频率和时间都会增加。堆内缓存一般使用软引用/弱引用来引用对象。使用这两种引用的好处是,当堆内存不足时,可以强制回收这部分内存,释放堆空间。堆内缓存最大的问题是重启时内存中的缓存数据会丢失。如果堆内缓存使用过多,再加上流量风暴,应用可能会被压垮。堆内缓存的实现一般有:GuavaCache、Ehcache等。堆外缓存,这个同学听的比较少,在Java堆外的内存中,不受GC控制,也不受大小限制heap,但只受机器内存的限制,所以使用时一定要小心。不当可能会导致内存泄漏!堆外内存需要序列化/反序列化,所以会比堆内缓存慢。其他应用缓存是指本服务以外的缓存,比如本地redis缓存。本地redis缓存是指在本地服务器上部署一组Redis,应用程序直接读取本机获取缓存数据,利用主从机制在多台机器之间同步数据。这种方式的优点是没有网络消耗,性能最优。分布式缓存如果数据量不大,使用本地redis缓存的架构是最优的。使用本地redis缓存最大的问题是:单机容量问题,多实例数据一致性问题,多实例缓存命中率降低,导致回源DB。如果遇到这样的问题,应该把数据切分,尽可能均匀的分布在服务器上,这就是分布式缓存。分布式缓存的常见分片策略包括:节点剩余一致性哈希虚拟槽分区我们最常见的Redis-Cluster集群使用虚拟槽分区来分片数据。让我们就此打住。关于Redis缓存,以后会有很多文章讨论,敬请期待!其他:缓存命中率缓存命中率对我们来说是一个非常重要的指标。如果我们使用缓存,就必须监控这个指标,看看缓存的工作状态。它的计算方法是:命中率缓存命中总读取次数,缓存命中率越高越好。如何提高缓存命中率?不同场景的数据我们应该有不同的缓存策略,比如:大促来临的时候,就应该提前缓存热点数据。这种方法称为缓存预热或缓存热加载。在案例1的基础上,热点缓存数据与普通缓存数据分离。这就需要前期的人工干预,后期的实时热点。发现;对数据进行分类,为不同类型的数据配置合适的过期时间;调整缓存粒度,通常缓存粒度越小,缓存命中率越高;增加存储容量,当容量不够时,会触发过期策略导致部分缓存数据失效,影响缓存命中率;缓存问题:缓存击穿【一句话概述】缓存击穿是指数据库和缓存都没有的数据,每次访问数据库都要经过缓存。停机时间。(强调没有数据+并发访问)我会继续点这里,后面会提供,敬请期待。缓存问题:缓存穿透【一句话概括】缓存穿透是指数据存在于数据库中,但不存在于缓存中。大量请求访问缓存中不存在的数据,最终请求命中DB可能导致DB崩溃。(强调单Key过期+并发访问)这里我继续点停,后面会提供,敬请期待。缓存问题:缓存雪崩【一句话概括】缓存击穿是指数据存在于数据库中,但不存在于缓存中。大量请求访问缓存中不存在的数据,最后请求命中DB可能会导致DB崩溃。(强调批量Key过期+并发访问)我会继续点这里,后面会提供,敬请期待。缓存问题:缓存一致性【一句话概括】缓存一致性是指缓存与DB之间的数据一致性。我们需要通过各种手段来防止缓存和DB不一致。我们需要保证缓存中的数据和DB中的数据是一致的或者说数据是最终一致的。这里我继续点到最后,后面会提供,敬请期待。缓存的其他问题缓存的好处对我们来说是非常有益的。用户的每一个请求都伴随着无数缓存的诞生,但是缓存也给我们带来了很多挑战,比如上面提到的一些棘手的问题:缓存磨损穿透、缓存击穿、缓存雪崩、缓存一致性。除此之外,我们还会涉及到一些其他的缓存问题,比如:缓存倾斜、缓存阻塞、缓存慢查询、缓存主从一致性问题、缓存高可用、缓存故障发现与故障恢复、集群扩缩容、BigKeyHotKey...今天只做一个缓存的开篇,具体细节留给后续章节。