缓存乃是一个用于临时存储数据的所在。当用户进行数据查询时,第一步便是在缓存中探寻,倘若找到了,便直接加以运用;要是未找到,就需前往数据的初始位置寻觅。因而,缓存实质上属于一种以空间换取时间的技术手段,借由数据在空间层面的重复,来加快数据的访问速度。
不过,伴随分布式以及云计算技术的不断发展,数据存储技术已然发生了天翻地覆般的变化。而且,不同的存储技术在价格和性能方面均存在极大的差异。所以,在针对性能展开软件设计的时候,如果我们未能做好多层级的缓存设计,不但有可能造成金钱的浪费,而且所获取的性能收益或许也难以达到理想状态。
缓存设计的通关之路
那么首先,我打算从两个问题入手,引领您了解缓存设计应在何时开展,以及通过对不同数据类型特性的对比分析,和您共同探讨怎样才能做好缓存设计。
好,第一个问题:在互联网应用服务中,运用缓存技术的目的难道仅仅是为了提升访问速度吗?实际上,我觉得并非所有的缓存都只是为了提速,因为在分布式系统里,缓存机制实则是系统级性能设计的一项重要权衡手段。例如,当某个数据库的负载较高,接近系统瓶颈时,我们能够运用缓存技术,将负荷分摊至其他数据库中,在此,使用缓存的目的,主要是实现负载均衡,而非提升访问速度。
第二个问题:一个大型系统中的数据种类繁多,那么是否需要为每种数据都规划缓存机制呢?其实完全没必要。在实际的业务场景中,系统所包含的业务数据极多,您不可能针对每种数据都去设计和实现缓存机制,一方面是投入的软件成本过高,另一方面也很可能无法带来较高的性能收益。所以,在开展缓存设计之前,您首先需要辨别出哪些数据访问对性能的影响较大。那么我们应当如何去辨别哪些数据需要缓存机制呢?
不过接下来,在识别出需要运用缓存机制的数据之后,您或许会发现,这些数据种类之间的特性差异极大,如果采用同一种缓存设计,实际上很难让软件性能达到最佳状态。所以在此,我为您总结了三种需要缓存机制的数据种类,分别是不变性数据、弱一致性数据、强一致性数据。了解这三种缓存数据种类的差异,以及对应的设计缓存机制的方法,您就掌握了缓存设计的核心要义。
好,下面我们就来具体了解下吧。
首先是不变性数据。不变性数据意味着数据永远不会发生变化,或者在相当长的一段时间内不会发生变化,所以我们也能够认定这部分数据是不变的。此类数据属于可以优先考虑运用缓存技术的一种数据类型,在实际的业务场景中数量众多。例如,Web 服务里的静态网页、静态资源,或者数据库表中列数据与 key 的映射关系、业务的启动配置等等,这些都能够被视为不变性数据。
而且,不变性数据还意味着实现分布式一致性会极为容易,我们能够为这些数据任选数据存储方式,也能够任选存储节点位置。故而,我们实现缓存机制的方式能够十分灵活,也会相对简单。比如在 Java 语言中,您可以直接使用内存 Caffeine,或者内置的结构体当作缓存均可。
另外这里需要您注意,当您针对不变性数据进行缓存设计时,其中的缓存失效机制可以采用永远不失效,或者基于时间的失效方式。而在采用基于时间的失效方式时,您还需要依据具体的业务需求,在缓存容量和访问速度之间做好设计实现方面的权衡。
弱一致性数据
第二种是弱一致性数据。它表示数据经常会发生变化,然而业务对于数据的一致性要求并不高,也就是说,不同用户在同一时间点看到并非完全一致的数据,都是能够被接受的。鉴于这类数据对一致性的要求相对较低,所以在设计缓存机制时,您只需达成最终一致性即可。这类数据在实际业务里也较为常见,例如业务的历史分析数据、一些搜索查找返回的数据等,即便最近的部分数据未被记录进去,影响也不大。
另外,快速辨别这类数据还有一个办法,那便是使用从数据库 Replica(复制)节点中读取的数据,其中大部分都属于这种类型的数据(很多数据库 Replica 节点的数据由于数据同步存在时延,是不满足强一致性要求的)。针对弱一致性的数据,我们通常采用的缓存失效机制是基于时间的失效方式,同时由于弱一致性的特点,您能够较为灵活地选择数据存储技术,比如内存 Cache,或者是分布式数据库 Cache。您甚至能够基于负载均衡的调度,来设计多层级缓存机制。
强一致性数据
第三种缓存数据类型是强一致性数据。其指的是数据会频繁发生变化,并且业务对数据库的一致性要求极高,也就是说当数据产生变更后,其他用户在系统内的任何地方,都应当看到的是更新后的数据。
那么,对于这种类型的数据,我通常不建议您使用缓存机制,因为这类数据运用缓存会较为复杂,并且极易引入新的问题。例如,用户能够直接提交和修改的各类数据内容,如果未同步修改缓存中的数据,就会引发数据不一致性的问题,导致较为严重的业务故障。
不过在某些特殊的业务场景中,比如,在个别数据访问频率极高的情况下,我们依旧需要通过设计缓存机制,来进一步提高性能。
因此针对这类强一致性数据,在设计缓存机制时,您需要尤其留意两点:
这种数据的缓存必须采用修改同步的实现方式。也就是说,所有的数据修改都必须保证能够同步修改缓存与数据库中的数据。
精确识别特定业务流程中,能够使用缓存获取数据的时长。因为有些缓存数据(比如一次 REST 请求中,多个流程都需要使用的数据)只能够在单次业务流程中使用,不能跨业务流程使用。
好了,以上便是三种典型的数据种类的缓存设计思路了。
这里您需要注意的是,使用缓存必定是以性能优化为目的,因此,您还需要运用评估模型来分析缓存是否达成了性能优化的目标。
那么具体是何种评估模型呢?我们来看一下这个性能评估模型的公式:AMAT = Thit + MR * MP。其中:AMAT(Average Memory Access Time),代表的是平均内存访问时间;Thit,指的是命中缓存之后的数据访问时间;MR,是访问缓存的失效率;MP,是指缓存失效后,系统访问缓存的时间与访问原始数据请求的时间之和。
另外这里您可能会留意到,AMAT 与原始数据访问之间的差值,代表的就是使用缓存所带来的访问速度的提升。而在一些缓存使用不当的场景下,增加的缓存机制很可能会造成数据访问速度下降的情况。所以接下来,我就通过真实的缓存设计案例,来引领您理解如何正确地使用缓存,以此助力您更有效地提升系统性能。
缓存设计的典型使用场景
好,在开始介绍之前呢,我还想为您说明一下,在真实的业务里,缓存设计的场景实际上有很多,在这里我的目的主要是让您明晰缓存设计的方法。
因此,我会从两个较为典型的案例场景着手,引领您理解缓存的运用。
如何做好静态页面的缓存设计?
在 Web 应用服务中,一个重要的应用场景便是静态页面的缓存使用。这里所说的静态页面,指的是在一个网站内,所有用户看到的都是相同的页面,除非重新部署,否则通常不会发生变更,比如大部分公司官网的首页封面等等。通常情况下,静态页面的访问并发量是比较大的,如果您不运用缓存技术,不但会导致用户响应时延较长,而且会给后端服务带来极大的负载压力。
那么针对静态页面,我们在使用缓存技术时,可以通过将静态缓存放置在距离用户较近的位置,来降低页面数据在网络上的传输时延。
现在,我们来看一个针对静态页面使用缓存设计的示意图:
图片
如图上所示,针对静态网页,首先您就能够在软件后端服务的实例中运用缓存技术,以此避免每次都要重新生成页面信息。然后,由于静态网页属于不变性数据,所以您可以使用内存或文件级缓存。
另外,针对访问量极大的静态页面,为了更进一步减轻对后端服务的压力,您还能够将静态页面置于网关处,接着利用 OpenResty 等第三方框架增添缓存机制,以保存静态页面。
除此之外,在网页中众多的静态页面或静态资源文件,还需要使用浏览器的缓存,来进一步提高性能。注意,这里我并非是建议您针对所有的静态页面,都需要设计三层的缓存机制,而是您要知晓,在软件设计阶段,通常就需要考虑如何进行静态页面的缓存设计了
后端服务如何设计数据库的多级缓存机制?
还有一个典型的缓存场景是针对数据库的缓存。现在的数据库通常都是分布式存储的,而且规模都比较大,在针对大规模数据进行查询与分析计算时,都需要花费一定的时间周期。因此,我们可以先识别出这些计算结果中可以使用缓存机制的数据,然后就可以使用缓存来提升访问速度了。下面是一张针对数据库缓存机制的原理图:
图片
从图上能够看到,内存级 Cache、分布式 Cache 均可充当数据计算分析结果的缓存。而且,不同级别的缓存访问速度存在差异,内存级的 Cache 访问速度能够达到微秒级别,甚至更优;分布式 Cache 访问速度通常小于毫秒级别;而对于原生数据库的查询与分析,通常大于毫秒级别。
因此,在具体规划缓存机制的时候,您就需要依照前面我所介绍的缓存使用原理,辨别出数据类型,进而选择并设计缓存实现机制。
另外,在通过缓存机制实现访问速度优化的过程中,我们主要关注的是不同层级缓存所带来的访问速度提升,并且在此处,不同层级缓存也能够存在于一个数据库中。
比如,在我参与设计的一个性能优化项目里,其 Cache 策略便是,使用 MongoDB 中的另外一个 Collection(集合),作为缓存查询分析,以此来优化性能。所以,您在进行缓存设计时,关注点应当置于不同的数据种类,以及不同层级缓存的性能评估模型上,而非仅仅关注数据库。只有如此,您才能够设计出更出色、更优良的性能缓存方案。