分布式系统之缓存的微观应用经验谈(三)【数据分片和集群篇】
前言
近几个月一直在忙些琐事,几乎年后都没怎么闲过。忙忙碌碌中就进入了2018年的秋天了,不得不感叹时间总是如白驹过隙,也不知道收获了什么和失去了什么。最近稍微休息,买了两本与技术无关的书,其一是 Yann Martel 写的《The High Mountains of Portugal》(葡萄牙的高山),发现阅读此书是需要一些耐心的,对人生暗喻很深,也有足够的留白,有兴趣的朋友可以细品下。好了,下面回归正题,尝试写写工作中缓存技术相关的一些实战经验和思考。
正文
在分布式Web程序设计中,解决高并发以及内部解耦的关键技术离不开缓存和队列,而缓存角色类似计算机硬件中CPU的各级缓存。如今的业务规模稍大的互联网项目,即使在最初beta版的开发上,都会进行预留设计。但是在诸多应用场景里,也带来了某些高成本的技术问题,需要细致权衡。本系列主要围绕分布式系统中服务端缓存相关技术,也会结合朋友间的探讨提及自己的思考细节。文中若有不妥之处,恳请指正。
为了方便独立成文,原谅在内容排版上的一点点个人强迫症。
第三篇这里尝试谈谈缓存的数据分片(Sharding)以及集群(Cluster)相关方案(具体应用依然以Redis 举例)
另见:分布式系统之缓存的微观应用经验谈(二) 【主从和主备高可用篇】(https://www.cnblogs.com/bsfz/)
一、先分析缓存数据的分片(Sharding)
(注:由于目前个人工作中大多数情况应用的是Redis 3.x,以下若有特性关联,均是以此作为参照说明。)
缓存在很多时候同 RDBMS类似,解决数据的分布式存储的基础理念就是把整个数据集按照一定的规则(切分算法)映射到多个节点(node)中,每个 node负责处理整体数据的一个子集。给缓存作 Sharding 设计,围绕基础数据的存储、通信、数据复制和整合查询等,很多时候比较类似 RDBMS中的水平分区(Horizontal Partitioning),事实上很多点在底层原理上是保持一致的。在缓存的分区策略中,最常见的是基于哈希的各种算法。
1.1 基于 Round Robin
实现思路是以缓存条目的标识进行哈希取余,如对缓存中的 Key 进行 Hash计算,然后将结果 R 与 node 个数 N 进行取余,即 R%N 用来指定数据归属到的 node索引。
个人认为在数据量比较固定并有一定规律的场景下,则可以考虑基于这种方式的设计。在落地实践前需要注意,这种方案看起来简洁高效,但却无法良好的解决 node的弹性伸缩问题,比如数量 N发生变化时均需要重新覆盖计算,存储的数据几乎是重新迁移甚至重置,对数据的支撑本身会有些勉强和局限。另外,早期使用手动进行预分区,后来增加了一些相应的路由策略可进行翻倍扩容等,都可考虑作为实践场景中的某些细小优化和辅助。
1.2 基于 Consistent Hashing
实现思路是将 node 进行串联组合形成一个 Hash环,每个 node 均被分配一个 token作为 sign,sign取值范围对应 Hash结果区间,即通过Hash计算时,每个 node都会在环上拥有一个独一无二的位置,此时将缓存中的Key做基本Hash计算,根据计算结果放置在就近的 node 上,且 node 的 sign 大于等于该计算结果。
这种策略的优势体现在,当数据更迭较大,动态调整node(增加/删除)只影响整个 Hash环中个别相邻的 node,而对其他 node则无需作任何操作。也就是说,当数据发生大量变动时,可以有效将影响控制在局部区间内,避免了不必要的过多数据迁移,这在缓存的条目非常多、部署的 node也较多的时候,可以有效形成一个真正意义上的分布式均衡。但在架构落地之前,这种方案除了必要的对 node 调整时间的额外控制,还需要权衡下缓存数据的体量与 node 的数量进行比较后的密集度,当这个比值过大时,数据影响也自然过大,这个就是不符合设计初衷的,不仅没有得到应有的优化,反而增加了一定的技术成本。
1.3 基于 Range Partitioning
实现思路实际是一种增加类似中间件的包装思想,首先将所有的缓存数据统一划分到多个自定义区间(Range),然后将这些 Range逐一绑定到关联服务的各个 node中,每个 Range将在数据变化时进行相应调整以达到均衡负载。
这其实并非是一个完全新颖的策略,但针对大数据的划分和交互做了更多的考虑。以 Redis的 Sharding算法类比,截止目前的集群方案(Redis Cluster,以3.x举例)中,其策略同样包含了 Range这一元素概念。Redis采用虚拟槽(slot)来标记,数据合计 16384个 slot。缓存数据根据 key进行 Hash归类到各个 node绑定的 slot。当动态伸缩 node时,针对 slot做相应的分配,即间接对数据作迁移。