缓存(Cache)是一种存储技术,可以存储数据,以便快速获取数据。缓存最重要的是两个特性:存储、快速获取。缓存的本质:「用空间换时间」,用快速存储的介质保存数据,以提升数据访问的速度。
在计算机的世界里,缓存被广泛应用于硬件(如 CPU 高速缓存、寄存器)和软件中(如浏览器缓存、CDN、应用程序中的缓存等)。
后台中的缓存
后台开发中,缓存的使用场景一般有:
提升接口响应速度:缓存相比 IO 请求、数据库查询速度要快得多,适当使用缓存提升响应速度
降低数据源服务的负载压力:将需要高并发读取的数据缓存起来,当相同请求进来时返回缓存数据,减轻数据源服务(如数据库、上游系统)的负载压力
减少计算资源的浪费:将需要复杂且耗时计算的结果缓存起来,减少相同请求导致的重复计算资源浪费
缓存策略缓存策略通常分为两种,通读缓存和旁路缓存。
1.1 通读(read-through)缓存在使用通读缓存时,应用程序会尝试从缓存中获取数据。如果该数据已存在于缓存中,那么缓存会直接返回该数据。若缓存中并未包含所需数据,那么缓存会自行访问数据源来获取数据,并将获取的数据返回给应用程序,同时将这份数据保存在缓存中。如此一来,当应用程序下次需要同样的数据时,就能够直接从 read-through 缓存中获取,无需再次访问数据源。
通读缓存的应用有很多,比如 CDN:
CDN(Content Delivery Network)即内容分发网络,是一种分布式的网络架构。依赖于服务商提供的广泛分布于各地的 CDN 服务器,通过把数据分发并缓存到各地的 CDN 服务器上,当用户请求数据时,CDN 会匹配离用户距离最近的节点并返回缓存数据,以此来提高用户访问资源的速度。CDN 一般用来加速静态资源的传输,比如图片、CSS、HTML 等内容。而动态的内容,如搜索结果、单据信息等则不合适,这类数据需要应用服务器实时计算后才能返回。
1.2 旁路(cache-aside)缓存与通读缓存不同,旁路缓存不直接与数据源打交道。应用程序尝试从旁路缓存获取数据,如果数据不存在,则返回空。应用程序自行请求数据源获取数据,并写入旁路缓存。这样下次相同的请求到达时,应用程序从旁路缓存中则会获取到数据。
通读缓存和旁路缓存这两种策略,一般来说都会灵活应用、互相包含。比如通读缓存中,对数据的查询和写缓存,使用的就是旁路缓存策略。而该实现被整体封装起来,对外表现的则是通读缓存策略。
在后台服务中,缓存的类型可以分为本地缓存和分布式缓存。
缓存类型2.1 本地缓存(local cache)本地缓存,与应用程序的进程有相同的生命周期,存放于应用程序的堆空间(heap)中。
优势:
使用简单;无外部依赖;读取速度快(无网络 IO 请求);
缺点:
空间小:应用程序的服务器资源有限,所以本地缓存的空间小;分布式一致性问题:如果后台服务是分布式架构的,那么不同的服务实例之间的本地缓存可能会有差异;无法持久化:本地缓存会随着进程结束而被销毁,无法持久化。
2.2 分布式缓存(remote cache)分布式缓存,也可理解为远端缓存。使用外部的缓存服务,独立部署,与应用程序解藕。
优势:
空间充足:外部存储一般空间都很充足;无分布式一致性问题:不同的服务实例连接同一个缓存服务,不存在一致性问题;主流的分布式缓存,如 redis,支持数据持久化和恢复,当缓存服务挂了可以恢复数据。
缺点:
引入外部依赖、需要部署和运维单独的缓存服务。
缓存淘汰策略由于缓存的空间是有限的,如果缓慢的空间被使用完了,则需要淘汰旧的数据,腾出空间给新的数据使用。缓存淘汰常用的几种策略有如下几种。
3.1 FIFO(First In First Out)算法FIFO 算法是最简单最好理解的,其策略是:先进先出,如果一个数据的写入时间越早,说明将来被访问的几率越低。因此 FIFO 算法优先淘汰最早写入的数据。
3.2 LRU(Least Recently Used)算法LRU 算法,即最近最少使用算法。如果一个数据最近被访问了,那么将来被访问的几率越高。反之,如果一个数据很久都没有访问,那么将来被访问的几率越低。其淘汰策略就是:优先淘汰最久没有被使用到的数据。LRU 通常使用双向链表+哈希表来实现。
3.3 LFU(Least Frequently Used)算法LFU 算法,即最少使用算法。如果一个数据被访问的次数越多,那么将来被访问的几率越高。反之,如果一个数据被访问的次数越少,那么将来被访问的几率越小。其淘汰策略就是:优先淘汰最少被使用的数据。LFU 算法可以使用小顶堆+哈希表来实现。