多级缓存架构

系列 - 缓存技术系列
警告
本文最后更新于 2023-07-10,文中内容可能已过时。

https://image.linux88.com/2024/04/30/16f11c3ad488b9147cb90a50e5774ecb.svg

多级缓存架构可以帮助提升数据访问的速度,减少数据库压力,改善用户体验。设计多级缓存架构需要考虑一些关键问题和要点。

问题 1: 应用多级缓存架构的时候,如果数据发生变化了,如何保证每一级及时更新数据?

解决这个问题通常需要使用到缓存更新策略。如主动更新、过期更新和定期更新等。例如,当数据修改后,可以采用主动更新策略,即业务在写数据库的同时,更新相关的缓存。这样可以保证缓存数据的实时性。

问题 2: 多级缓存大大增加了架构复杂度,直接用分布式缓存不是更简单么?

分布式缓存的确可以简化架构,但是不同级别的缓存可以为不同的数据访问需求提供服务。例如,热点数据可以存储在内存缓存(如 Redis)中,频繁访问但不太敏感的数据可以存储在本地缓存(如 EHCache),而对于一些较大的,不经常访问的数据,可以使用更慢但更便宜的硬盘缓存。多级缓存可以使得系统更灵活,更好地满足不同的性能需求。

问题 3: 是否所有业务都要按照这个多级缓存架构来做?

并非所有的业务都需要多级缓存架构。对于一些并发请求较低,数据更新频率较高的业务,使用多级缓存架构可能会增加系统复杂度,而并不能带来显著的性能提升。是否使用多级缓存架构,需要根据具体业务需求和系统的性能要求来决定。

设计关键:

  1. 性能需求: 不同级别的缓存有不同的性能特点,要根据业务的性能需求来选择合适的缓存级别和类型。
  2. 架构复杂度: 使用多级缓存架构会增加系统复杂度,需要在性能提升和复杂度增加之间找到平衡。
  3. 数据一致性: 在多级缓存架构中,数据一致性是一个需要考虑的问题。需要确保当数据发生变化时,所有级别的缓存都能及时更新。
  4. 容量和成本: 不同级别的缓存有不同的容量和成本,需要根据业务的需求和预算来选择合适的缓存级别。

[!question] 存储系统也有缓存,可否当做第 6 级缓存?

https://image.linux88.com/2024/04/30/f06e70ff26b844b36408af8fd30d2e4d.svg

[!question] 为什么首先就考虑去掉 CDN?

  • CDN 需要成本

https://image.linux88.com/2024/04/30/881aeb25207f78aa9a8dd774a9e4c136.svg

[!question] 1)为什么去掉应用内缓存而不是去掉分布式缓存?

  • 应用内缓存性能最高,但是代码和维护复杂度会变高

2)可否继续去掉 Web 容器缓存和 App/浏览器缓存?

  • 不需要,这两个缓存成本很低,实现复杂度低

App 本地缓存,就是将应用程序在运行过程中产生或使用的数据暂时保存在手机本地,以便后续直接读取,从而减少对远程服务器的请求,提高应用性能,减少网络带宽的消耗。

应用场景:

  • 离线数据访问:在没有网络或网络环境较差的情况下,用户依然可以查看一些已经被缓存下来的数据,提高用户体验。
  • 减轻服务器压力:如果一些数据频繁地从服务器请求,而数据又没有变化,那么将数据缓存在本地可以大大减少对服务器的请求。
  • 提高数据加载速度:读取本地缓存数据的速度远快于从远程服务器获取数据,所以可以大大提高应用的运行效率。

常见技术:

  1. SQLite 缓存:SQLite 是一种嵌入式数据库,是一种轻量级的数据库,常常用来作为 App 在手机端的存储解决方案。将数据保存在 SQLite 中,可以方便地进行增删改查操作。
  2. 本地文件缓存:将数据直接写入本地文件,当数据量较小且读写操作较简单时,文件缓存是一种高效的缓存方式。
  3. 图片缓存:图片缓存是移动应用中常见的一种缓存方式,通过缓存已加载的图片,可以避免重复下载同一张图片,节省流量并提高加载速度。常用的图片缓存库有 Picasso(Square 开源),Fresco(Facebook 开源)以及 Glide(Google 开源)。这些库不仅提供了图片缓存的功能,还提供了图片加载优化、图片变换等功能。

HTTP 本地缓存是根据 HTTP 协议规定,允许客户端对请求过的网络资源进行本地存储,当下次请求同样的资源时,可以直接从本地缓存中读取,无需再次从服务器获取,从而减少网络请求,提高加载速度。

应用场景: HTTP 本地缓存主要用于 HTTP 资源的缓存,包括网页、图片、脚本、样式表等各种静态资源。通过利用 HTTP 本地缓存,可以有效地减少网络流量,提高网页加载速度,改善用户体验。

具体实现: HTTP 协议为了支持本地缓存,定义了一些特殊的头部字段,包括:

  1. Cache-Control:通过 Cache-Control 头部字段,服务器可以指示客户端缓存资源的行为。例如,Cache-Control 可以设为"private"表示资源只能被特定用户缓存,设为"no-cache"表示每次请求都要向服务器验证,设为"max-age"则指定资源在客户端缓存的最大存活时间。
  2. ETag/If-None-Match:服务器为每个资源生成一个唯一的 ETag 值(实体标签),客户端在请求资源时会将之前缓存的 ETag 值放在 If-None-Match 头部字段中一并发送给服务器。服务器通过比较 ETag 判断资源是否有修改:如果 ETag 值没有改变,说明资源没有修改,服务器返回 304 状态码,告诉客户端直接使用本地缓存;如果 ETag 值改变,说明资源已修改,服务器则返回新的资源内容。

[!question] App 通过 HTTP 接口获取的业务数据用哪种方式缓存比较好?

  • app 缓存 比 HTTP 好,原因比较灵活

Web 容器缓存,也常被称为 Web 服务器缓存,是一种在 Web 服务器上保存静态资源副本以提高处理速度和效率的技术。这种缓存一般用于存储不经常更改的文件,如图片、JavaScript 文件、CSS 样式表等。

作用:

  1. 提高性能:由于 Web 服务器缓存将常用的静态资源保存在内存或者磁盘上,当用户请求这些资源时,服务器无需访问硬盘或者其他后端服务器来获取资源,大大减少了读取和传输的时间。
  2. 减轻服务器压力:缓存的使用减少了对服务器的请求次数,这不仅减少了服务器的负载,也减少了网络带宽的使用。

实现方式: Web 容器缓存一般配合 HTTP 协议中的缓存控制头字段(如 Cache-Control、ExpiresETagLast-Modified等)来实现。当客户端首次请求某个资源时,服务器会在响应中添加这些头字段,并将资源的副本保存在缓存中。之后,当客户端再次请求同一资源时,服务器可以直接从缓存中返回资源,或者利用头字段进行缓存验证,以决定是否需要返回新的资源副本。

Web 容器如 Apache、Nginx 等都有相应的模块或者配置可以用于实现静态资源的缓存。例如,在 Nginx 中,我们可以使用expires指令来设置静态资源的缓存时间,使用open_file_cache指令来设置静态资源缓存的大小和过期时间等。

需要注意的是,Web 容器缓存虽然能够提高性能,但也有可能造成数据的过期和不一致问题。因此,使用时需要根据具体的业务需求和场景进行合理的配置和管理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
proxy_cache_path /cache/nginx/cache levels=1:2 keys_zone=yum:500m inactive=1000d max_size=80g;

server {
    server_name www.baidu.com;
    proxy_cache yum;
    proxy_cache_valid 200 304 302 1000d;
    proxy_cache_valid any 1m;
    proxy_ignore_headers Set-Cookie;
    proxy_ignore_headers Cache-Control;
    proxy_ignore_headers Expires;
    proxy_hide_header Cache-Control;
    proxy_hide_header Set-Cookie;
    proxy_hide_header Expires;

    location / {
        proxy_pass http://www.baidu.com/;
    }
}

应用缓存定义 应用缓存是指应用程序在本地存储系统或内存中储存数据,以便更快地访问。它主要用于缓存经常被访问的数据,减少与远程服务器或数据库的交互,从而加速应用程序的运行速度。

应用场景 应用缓存的使用场景非常广泛,包括但不限于以下几种:

  • 缓存经常被查询的数据库查询结果,如用户信息、商品信息等
  • 缓存系统配置信息、元数据
  • 缓存来自远程服务的调用结果
  • 缓存经常被访问的计算结果,如热门排行榜、热门推荐等

常见技术

  • 进程内缓存:顾名思义,就是在一个进程的内存中进行缓存。这种方式的缓存速度快,但是存储容量受到进程内存大小的限制,而且无法实现多个进程间的数据共享。Java 世界中,常见的进程内缓存技术有 ConcurrentHashMap,以及一些开源缓存库如 Oscache、Ehcache 等。
  • 进程外缓存:这种方式的缓存存储在进程之外,可以是内存,也可以是磁盘。这种方式的缓存可以被多个进程共享,存储容量也更大。常见的进程外缓存有 Redis,Memcached 等。
  • 本地磁盘 SSD 缓存:这种方式的缓存使用本地的固态硬盘进行存储,相比内存缓存,它的存储容量更大,而且不会因为进程重启而丢失,但是访问速度稍慢。常见的 SSD 缓存技术有 RocksDB,LevelDB 等。

分布式缓存定义 分布式缓存是一个跨多个网络节点的内存数据库,可以将数据存储在内存中,从而提供更快的访问速度和更高的处理性能。分布式缓存的设计允许许多用户同时访问,并保持数据的一致性和完整性。

应用场景 分布式缓存主要用于以下几种场景:

  1. 大规模数据处理:在大规模数据处理中,分布式缓存可以用于存储计算结果,以便在之后的计算中快速获取。
  2. Web 应用:在 Web 应用中,分布式缓存可以用于存储用户会话、购物车数据等频繁访问的信息,提高响应速度和用户体验。
  3. 缓解数据库压力:当数据库无法承受大量请求时,可以使用分布式缓存来存储热点数据,减轻数据库的压力。

具体实现

  1. Redis:Redis 是一个开源的,支持网络,可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。Redis 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启后可以再次加载进行使用。
  2. Memcached:Memcached 是一个高性能,开源的内存对象缓存系统,原始设计用于缓解大型动态 Web 应用后端数据库的负载。Memcached 是一个简单的键值存储,但不支持持久化存储。

SSD(Solid State Drive)是一种固态硬盘技术,与传统的硬盘(HDD)相比,其主要优势在于:

  1. 速度:SSD 的读写速度远超过传统的硬盘。SSD 使用闪存来存储数据,访问速度可以在几十微秒内完成,远快于硬盘的几毫秒。因此,SSD 可以作为 RAM(随机访问存储器)和 HDD(硬盘驱动器)之间的高速缓存,用于存储经常访问的数据,从而提高系统的性能。
  2. 可靠性:SSD 没有机械运动部件,更耐用,抗震性更好,且发热量小,噪音低。这些特性使其在持久性和稳定性方面,比传统的硬盘有更大的优势。
  3. 低延迟:由于 SSD 没有机械部件,数据访问的延迟时间极短,这使得 SSD 非常适合做缓存,特别是对于高性能计算和实时系统来说。
  4. 节能:与 HDD 相比,SSD 的功耗更低,这对于移动设备和数据中心来说都是重要的考量。

因以上的优势,SSD 非常适合用来作为缓存,例如在数据库应用中,SSD 常被用于存储热点数据,从而提高系统的读取性能。

https://colin-scott.github.io/personal_website/research/interactive_latency.html

以上描述详细地解析了 Memcached 和 Redis 在不同情况下的适用性:

  1. 对象大小:Memcached 最初设计用于缓存较大的对象(例如整个网页的 HTML),而 Redis 虽然也可以存储较大的对象,但是由于它是单进程的,处理大量大对象的读写可能会对性能造成影响。所以,如果有大量大对象的读写需求,Memcached 可能会是更好的选择。
  2. 数据结构的复杂性:相比 Memcached,Redis 支持更多的数据类型和更复杂的数据结构(例如列表,集合,哈希表等),并且 Redis 支持事务,可以保证一系列操作的原子性。所以,如果业务逻辑中有数据库难以满足的需求,而 Redis 可以很好满足的情况,Redis 可能是更好的选择。
  3. 持久化需求:Redis 支持数据持久化,可以将内存中的数据保存在磁盘中,重启后可以再次加载使用,这对于某些数据价值高,生成代价大的缓存数据来说非常有用。而 Memcached 数据全部存放在内存中,服务重启或者异常关闭后,所有的数据都会丢失。所以,如果缓存数据的生成代价很高,丢失可能引起严重的系统问题,那么 Redis 可能是更好的选择。而如果短时间丢失部分缓存并不会造成大的影响,Memcached 可能会更适用,因为它的结构更简单,易于管理。 在实际使用中,根据具体的业务需求和系统环境选择最合适的缓存技术是非常重要的。

[!question] 如果不确定是否有上面描述的场景,应该选哪个?

  • 不确定就选 Redis

相关内容