问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

架构思维:分布式缓存_通读缓存/旁路缓存 & 一致性哈希(下)

创作时间:
作者:
@小白创作中心

架构思维:分布式缓存_通读缓存/旁路缓存 & 一致性哈希(下)

引用
CSDN
1.
https://blog.csdn.net/yangshangwei/article/details/145864746

分布式缓存是提升系统性能的关键技术之一,它通过将数据存储在高速缓存中,显著提高了数据访问速度。本文将深入探讨分布式缓存的两种主要类型:通读缓存和旁路缓存,并讨论它们的使用场景、优势与挑战。特别是在分布式系统中,如何通过一致性哈希算法优化缓存的路由,保证系统的可扩展性与高效性。

一、通读缓存与旁路缓存

1. 通读缓存

通读缓存是一种代理模式,缓存充当客户端与原始服务器之间的中介。用户在访问数据时,始终先请求缓存,如果缓存中存在所需数据,则直接返回;如果没有,再向原始服务器请求数据。常见的通读缓存类型包括代理缓存、反向代理缓存、以及内容分发网络(CDN)缓存。

  • 代理缓存 位于客户端,代理客户端的请求,通过缓存已有数据来减少对原始服务器的访问。
  • 反向代理缓存 位于数据中心,客户端请求经过反向代理,首先检查缓存是否命中,若无则转发请求至原始服务器。
  • CDN 缓存 是全球分布的缓存网络,用户请求的数据会从离其最近的缓存节点返回,显著减少访问延迟。

通读缓存的最大优点在于其透明性,客户端无需关心原始数据源的位置,所有请求都经过缓存处理,提升了数据访问的效率。

2. 旁路缓存

旁路缓存与通读缓存有所不同。旁路缓存的工作方式是客户端首先访问缓存,如果缓存中没有数据,则直接向后端数据源请求,并在获取到数据后将其写入缓存。下次或其他客户端访问时,数据将从缓存中返回。

与通读缓存相比,旁路缓存不代理用户请求,而是由客户端主动管理缓存的读写操作。这种方式特别适用于数据的写入和读取频率较低的场景。

各种介质数据访问的延迟,以便对数据的存储、缓存的特性以及数据的访问延迟有一个感性的认识

访问本地内存大概需要 100ns 的时间;使用 SSD 磁盘进行搜索,大概需要 10万ns 时间;数据包在同一个数据中心来回一次的时间,也就是在同一个路由环境里进行一次访问,大概需要 50万ns 时间,也就是 0.5ms;使用非 SSD 磁盘进行一次搜索,大概需要 1000万ns,也就是 10ms 的时间;按顺序从网络中读取 1MB 的数据也是需要 10ms 的时间;按顺序从传统的机械磁盘,即非 SSD 磁盘,读取 1MB 数据,大概需要 30ms 的时间;跨越大西洋进行一次网络数据传输,一个来回大概需要 150ms 的时间。其中,1s 等于 1000ms,等于 10亿ns

二、缓存的效率与优化

尽管缓存技术本身简单易用,并能显著提高性能,但在实际应用中,合理使用缓存对象至关重要。

1. 频繁修改的数据

缓存的设计理念是“一次写入,多次读取”。如果数据更新频繁且读取频率较低,则缓存效果可能不理想。例如,频繁更新的数据可能在缓存中未被读取就失效,增加了系统的负担,因此这种数据通常不适合缓存。

2. 没有热点的访问数据

缓存的最大价值体现在热点数据的存储。如果没有热点数据,缓存可能会频繁清空,不会有效提升性能。热点数据是指那些频繁访问的热门数据,如电商平台的热销商品、社交媒体上的热门帖子等。

3. 数据一致性和脏读

缓存的数据可能与数据库中的数据不一致。在一些业务场景中,尤其是对数据一致性要求极高的系统,使用缓存时需要特别小心。一种常见的解决方案是设定缓存失效时间(TTL),确保数据在合理的时间内过期,或者使用失效通知机制,即数据更新时清除相关缓存,确保缓存中的数据始终保持最新。

4. 缓存雪崩等问题

缓存雪崩发生在缓存系统崩溃时,导致大量请求直接转发到数据库,从而给数据库带来巨大的负载压力,可能导致系统崩溃。为了防止这种情况,常用的策略包括缓存预热、设置不同的缓存过期时间以及对缓存进行分级处理。

三、分布式缓存与一致性哈希

分布式缓存是将缓存数据分散存储在多个节点上,常用于高并发、大规模的数据存储场景。由于缓存数据分布在多个服务器上,如何确保数据的正确路由和访问成为一个关键问题。

1. 缓存的路由问题

在分布式缓存中,如何根据请求的缓存键(key)找到正确的缓存服务器是一个挑战。一个常用的路由算法是哈希算法,具体而言,采用“余数取模”算法。通过计算缓存键的哈希值,并将其对缓存服务器的数量取模,可以确定数据应存储在哪个服务器上。

当需要进行分布式缓存访问的时候,依然是以 Key、value 这样的数据结构进行访问。如上图所示的例子中就是 BEIJING 作为 Key,一个 DATA 数据作为它的 value。当需要进行分布式对象访问的时候,应用程序需要使用分布式对象缓存的客户端 SDK。比如说 Memcached 提供的一个客户端 API 程序进行访问,客户端 API 程序会使用自己的路由算法进行路由选择,选择其中的某一台服务器,找到这台服务器的 IP 地址和端口以后,通过通讯模块和相对应的服务器进行通信。

因为进行路由选择的时候,就是使用缓存对象的 key 进行计算。下一次使用相同的 key 使用相同路由算法进行计算的时候,算出来的服务器依然还是前面计算出来的这个服务器。所以通过这种方法可以访问到正确的服务器进行数据读写。服务器越多,提供的缓存空间就越大,实现的缓存效果也就越好。通过集群的方式,提供了更多的缓存空间。

那么,路由算法又是如何进行服务器路由选择的?主要算法依然是上面讲到的哈希表的路由算法,也就是取模算法。

比如说,我们这里缓存服务器集群中有 3 台服务器,key 的哈希值对 3 取模得到的余数一定在 0、1、2 三个数据之间,那么每一个数字都对应着一台服务器,根据这个数字查找对应的服务器 IP 地址就可以了。使用余数取模这种方式进行路由计算非常简单,但这种算法也有一个问题,就是当服务器进行扩容的时候,比如说我们当前的服务器集群有 3 台服务器,如果我们 3 台服务器不够用了,需要添加 1 台服务器,这个时候对 3 取模就会变成对 4 去取模,导致的后果就是以前对 3 取模的时候写入的数据,对 4 取模的时候可能就查找不到了。

然而,余数哈希算法在扩容时存在问题。比如,当新增缓存服务器时,原本按照余数哈希算法映射的数据可能会被分配到新的服务器上,导致部分缓存数据丢失,造成系统的不稳定。这就是所谓的“缓存雪崩”。

我们添加服务器的主要目的是提高它的处理能力,但是不正确的操作可能会导致整个集群都失效。解决这个问题的主要手段是使用一致性哈希算法。

2. 一致性哈希算法

为了解决余数哈希的扩容问题,引入了一致性哈希算法。一致性哈希通过构建一个哈希环来优化路由,服务器节点通过计算哈希值将自己映射到环上,数据的缓存键也会映射到哈希环中,顺时针查找距离最近的服务器即可。

一致性哈希的一个重要优势是,当集群增加或减少服务器节点时,仅有部分数据会重新映射到新的节点,从而减少缓存失效的范围,避免了大规模的数据迁移。

一致性哈希和余数哈希不同,一致性哈希首先是构建一个一致性哈希环的结构。一致性哈希环的大小是 0~2 的 32 次方减 1,实际上就是我们计算机中无符号整型值的取值范围,这个取值范围的 0 和最后一个值 2 的 32 次方减 1 首尾相连,就构成了一个一致性哈希环

对每个服务器的节点取模,求它的哈希值并把这个哈希值放到环上,所有的服务器都取哈希值放到环上,每一次进行服务器查找路由计算的时候,把 key 也取它的哈希值,取到哈希值以后把 key 放到环上,顺时针查找距离它最近的服务器的节点是哪一个,它的路由节点就是哪一个。通过这种方式也可以实现,key 不变的情况下找到的总是相同的服务器。这种一致性哈希算法除了可以实现像余数哈希一样的路由效果以外,对服务器的集群扩容效果也非常好。

在一致性哈希环上进行服务器扩容的时候,新增加一个节点不需要改动前面取模算法里的除数,导致最后的取值结果全部混乱,它只需要在哈希环里根据新的服务器节点的名称计算它的哈希值,把哈希值放到这个环上就可以了。放到环上后,它不会影响到原先节点的哈希值,也不会影响到原先服务器在哈希环上的分布,它只会影响到离它最近的服务器,比如上图中 NODE3 是新加入的服务器,那么它只会影响到 NODE1,原先访问 NODE1 的 key 会访问到 NODE3 上,也就是说对缓存的影响是比较小的,它只会影响到缓存里面的一小段。如果缓存中一小部分数据受到了影响,不能够正确的命中,那么可以去数据库中读取,而数据库的压力只要在它的负载能力之内,也不会崩溃,系统就可以正常运行。所以通过一致性哈希算法可以实现缓存服务器的顺利伸缩扩容。

3. 虚拟节点优化

但是一致性哈希算法有着致命的缺陷。我们知道哈希值其实是一个随机值,把一个随机值放到一个环上以后,可能是不均衡的,也就是说某两个服务器可能距离很近,而和其它的服务器距离很远,这个时候就会导致有些服务器的负载压力特别大,有些服务器的负载压力非常小。同时在进行扩容的时候,比如说加入一个节点 3,它影响的只是节点 1,而我们实际上希望加入一个服务器节点的时候,它能够分摊其它所有服务器的访问压力和数据冲突。

所以对这个算法需要进行一些改进,改进办法就是使用虚拟节点。也就是说我们这一个服务器节点放入到一致性哈希环上的时候,并不是把真实的服务器的哈希值放到环上,而是将一个服务器虚拟成若干个虚拟节点,把这些虚拟节点的 hash 值放到环上去。在实践中通常是把一个服务器节点虚拟成 200 个虚拟节点,然后把 200 个虚拟节点放到环上。key 依然是顺时针的查找距离它最近的虚拟节点,找到虚拟节点以后,根据映射关系找到真正的物理节点。

第一,可以解决我们刚才提到的负载不均衡的问题,因为有更多的虚拟节点在环上,所以它们之间的距离总体来说大致是相近的。第二,在加入一个新节点的时候,是加入多个虚拟节点的,比如 200 个虚拟节点,那么加入进来以后环上的每个节点都可能会受到影响,从而分摊原先每个服务器的一部分负载。

一致性哈希的一个优化措施是引入虚拟节点。将每台真实的服务器节点虚拟化成多个虚拟节点,从而使哈希环的分布更加均匀,避免负载不均。通过虚拟节点,扩容时影响范围会更小,缓存的均衡性和可扩展性得到进一步提升。

四、总结

缓存是系统性能优化中不可或缺的部分,它可以显著提高数据访问速度,减轻数据库负担,提升系统整体吞吐量。然而,缓存的合理使用需要考虑多个因素,如数据的一致性、频繁修改的数据以及热点数据的设计。在分布式环境中,通过一致性哈希和虚拟节点优化,可以有效解决缓存路由问题和扩容难题,保证系统的稳定性和高效性。

缓存的设计和实现不仅仅是一个简单的技术问题,更是架构师需要精心规划和调整的一个重要方面。合理的缓存策略能够帮助系统在高并发、大数据量的场景下保持高效与稳定,成为架构优化的关键手段。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号