本地分布式缓存一致性系统设计方案

此前我们发布的文章缓存与主副本数据一致性系统设计方案。讲解了如何在系统设计中选择缓存与主副本数据一致性处理模式以及如何正确的实现相应模式。其中提到的问题与解决方案基本可以覆盖各规模研发团队 90% 的业务场景。 采用缓存提升系统性能,提升系统并发度,是我们在系统设计角度下通常会想到的应用场景。在普遍降本增效的大环境下,服务器成本也逐步成为了我们选择为系统添加缓存的参考因素。 # 缓存用的好,降本又增效 我们以云厂商两类产品的定价做为成本参考,如下面两张图是国内头部云厂商的产品基础版本的价格截图(截取自24年12月03日)。第一张图为关系型数据库 MySQL 版本价格,选择高可用系列标准版 4C8G 以及 50GB 存储容量,付费方式为包年包月制一年固定费用为 7358.40 元。第二张图为缓存产品 Redis 开源版本价格,选择标准版 4GB 主从双副本,付费方式依然为包年包月制一年固定费用为 2448.00 元。 以上配置对于系统设计来讲,属于提供基本高可用保障的规格,其中关系型数据库的固定成本约为缓存产品的 3 倍。你可能会觉得这个价格表对比不公平,缓存的容量与数据库差距过大,所以成本才如此显著。当然如果想达到与数据库一致的存储容量,使用缓存的成本也将会是数倍于数据库。但是在实际业务场景下,会被高频访问与使用的数据可能不足总数据量的 20% 。 如果你所负责的系统属于基础系统,也是存在绝大部分数据存在高频高并发的访问场景。通常为了防止缓存穿透等问题,我们在部署架构上也会增加数据库节点,来承载这些以外流量,防止节点负载过高引发的系统问题。如果仅仅依靠扩展 MySQL 节点来承载系统负载,那么成本开销无疑也是巨大的。 此前本人整理的 Shopee 百亿级商品数据如何平稳实现千万级服务器成本缩减一文,便介绍了 Shopee 研发团队如何通过提升缓存命中率进而提高缓存利用率,促使数据库节点数量下降进而降低成本的案例。所以,如果你所在的团队面临控制硬件成本资源的挑战时,不妨考虑一下采用缓存的来缩减数据库的方案。同时还会带来系统性能的提升,可谓降本增效的典范。 当然缓存所能够承载的系统负载也是有限的,随着流量的增加缓存服务器的成本也会随之增加。为了应对此类情况,我们又可以采取应用本地缓存的方案,来降低缓存服务的负载。因为应用服务的成本通常价格相对较低,所以也是进一步控制成本的选择。 如下图所示为国内头部云厂商的轻量应用服务器的产品价格(截取自24年12月03日),即便按照官方折扣价一台 2 核 4G 的应用服务器一年的固定成本也仅需 816 元,上图同等价位的缓存服务可以组一个 3 台应用服务的集群。同样在真实环境下,50% 以上的应用服务其内存利用率都是不高的。长期为数据分配几十,甚至上百 MB 的空间是可能的,这不仅可以降低缓存系统的负载,还可以进一步带来系统性能的提升。 # 本地缓存的数据一致性问题 为系统引入本地缓存,又会带来颇具挑战的数据一致性问题。而为了解决数据一致性问题所带来的工程复杂度,常常让我们忽视掉其经济价值。其实在真实场景下,绝大多数系统中短时数据一致性问题都是可以容忍的。 在 Shopee 百亿级商品数据如何平稳实现千万级服务器成本缩减#多级缓存压缩缓存成本中提到 Shopee 研发团队结合自身实际场景,采用了一个颇具性价比的方案来实现本地缓存。大致思路为通过指定时间窗口内数据访问阈值以及本地缓存短 TTL 的方式实现本地缓存的使用。 可参考下图辅助理解,应用服务针对每个缓存记录维护一个计数器,起初请求穿过应用服务查询缓存,当单位时间内请求数量达到阈值(如:100QPS),便从缓存中加载数据至本地缓存,此后单位时间内(如:1S,S - 秒)请求查询本地缓存,本地缓存失效后查询缓存系统并重启计数。当然也可以在查询本地缓存时重启计数,当本地缓存失效时,如果请求依然达到阈值,再次加载数据至本地缓存。 ...

十二月 20, 2024 · HAibiiin

缓存与主副本数据一致性系统设计方案

对应用系统来讲,为提升系统整体性能与并发度,通常会采取为系统添加基于内存的存储系统方案。由该存储系统充当缓存(本文余下内容会采用“缓存 / Cache”指代此类存储系统),实现对应用系统整体的性能提升,并带来更高的并发表现。 通常在引入缓存后,加上以数据库为代表的数据存储系统,势必会导致系统内存在两个数据存储系统。因为两个数据存储系统的存在,从架构上便构成了主从架构。因为数据需要在两个副本之间传递复制,这一行为带来了数据一致性挑战。面对主从副本数据一致性解决方案主要分为两类,分别属于 强一致性解决方案 和 弱一致性解决方案 。 本文接下来讨论的方案属于弱一致性,对于弱一致性而言,最简单的解决方式是对所有非主副本的数据变更,均通过对主副本拷贝来实现。 如果你是一名有经验的工程师,对于缓存与数据库一致性方案一定听说过 Cache-Aside Pattern ,该模式以数据库做为数据的主副本,同时也是应用系统广泛采纳的设计方案。Cache-Aside 模式不会主动将数据复制到缓存中,而是在缓存未查询到数据时,从数据库复制到缓存中,所以又名 Lazy Loading 模式。 除此以外,你可能也听过 Read-Through 与 Write-Through 两种模式。这两种模式依赖缓存系统的能力,通过缓存系统内置能力实现对数据库的查询与更新操作,而应用系统无需关心对数据库的变更,只与缓存打交道。在应用系统视角下好像数据库不存在一样,将缓存看作是数据主副本。但现实往往未必如此理想,所以在本专栏文章中给出的系统设计方法,会避免技术与组件的特性,尽可能做到技术中立。 在实现 Cache-Aside 模式的方案中,主要争议点在数据变更场景(数据变更存在三种,分别是数据的新增、修改和删除)下数据同步到缓存的方式。两种方式最核心的区别是数据复制到缓存的时机不同。 一种会主动清理缓存中数据,依赖于数据查询时从数据库复制到缓存中。另一种会主动将数据复制到缓存中,确保数据常驻缓存。 # Cache-Aside 模式下数据读取方式 在探讨两种数据变更方式之前,我们先熟悉一下 Cache-Aside 模式下数据读取的方式。其执行过程如下: 当对某条数据记录的查询请求到来时,应用服务先确认该数据记录是否存在于缓存中; 如果数据记录在缓存中则直接返回查询结果; 如果数据记录不在缓存中,则查询主副本(数据库)获取该数据记录; 将主副本数据记录复制到缓存中,并返回该数据记录的查询结果。 结合下图与下方的注释说明,可以进一步直观的理解数据读取时的的过程。 如上图所示我们通过 Server - 1 与 Server - 2 两个应用服务来分别说明两种数据读取场景(本文中的配图均会采取此种表示,后续对于相同内容不再赘述): 缓存中存在查询记录数据: 图中 Server - 1 欲查询数据记录 1 的数据,如图中红色字体所示; 与 Server - 1 相关的数字 1-2 表示 IPC 请求与响应过程,其中可以将线条的斜率理解为该过程的耗时,斜率越大耗时越久; 绿色线条表示缓存执行数据记录查询操作的耗时,如 query cache time 所示; server - 1 query data process time 所代表的蓝色线条为执行本次缓存记录查询操作的总耗时; 缓存中不存在查询记录数据: 图中 Server - 2 欲查询数据记录 2 的数据,如图中红色字体所示,但缓存中不存在该数据; 与 Server - 2 相关的数字 1-6 表示 IPC 请求与响应过程,其中可以将线条的斜率理解为该过程的耗时,斜率越大耗时越久; 黑色线条表示数据库执行记录查询操作的耗时,即 i:query data time 所示; 蓝色线条表示应用服务执行操作耗时,如 server process time 所示,均表示收到 IPC 成功响应后准备发起后续 IPC 请求的耗时; 绿色线条表示缓存执行数据记录修改操作的耗时,即 iii:modify cache time 所示; 其中 server - 2 query data process time 所代表的蓝色线条为执行本次数据记录查询操作的总耗时; # Cache-Aside 模式下两种数据变更实现方式 上文中提到两种实现方式最核心的区别是数据复制到缓存的时机不同。因为时机不同使得两种实现方式差异明显。接下来我们分别探讨两种实现方式。 ...

十二月 14, 2024 · HAibiiin