【CS神书笔记】《数据密集型应用系统设计》(五)—— 数据分区 Data Partitioning(第6章)

开篇语:为什么我们要重读经典?

随着数据规模的爆炸性增长,我们早已进入了PB级数据时代。无论是亚马逊的商品目录、Facebook的社交图谱,还是字节跳动的短视频推荐,单一服务器已无法承载这些海量数据。而数据分区(Partitioning),作为一种将庞大数据集分割为可管理碎片的技术,成为了构建大规模数据系统的关键基石。

《数据密集型应用系统设计》第六章深入剖析了这项看似直观却蕴含众多微妙挑战的技术。当我们将数据拆分到多台机器上时,不仅要考虑如何平衡各节点负载,还要思考如何高效地定位和访问分散的数据。这些问题的答案,在8年前的DDIA中已有精辟论述,而在今天的微服务和云原生时代,这些洞见变得愈发重要。

这本书适合所有后台开发工程师、大数据工程师,也很适合面试前复习系统设计的同学,或者是想要提升计算机底层认知的同学。



一、本系列的特殊视角

这不是普通的读书笔记,而是一个后端开发的深度批注版。你将看到:

  1. 理论到实践的映射:比如"为什么MongoDB和Elasticsearch选择了不同的分区策略?这与它们的查询模式有何关联?"
  2. 行业演进验证:书中对分区再平衡技术的讨论如何在最新的云原生数据库中得到实践和改进。例如,从最初的Cassandra一致性哈希到现代数据库的自适应分区技术,我们将看到理论如何指导实践的演进。
  3. 反常识洞见:“为什么有时候选择次优的分区策略反而能提高系统整体可用性?” 比如在某些场景下,放弃范围分区的高效查询优势,转而选择哈希分区来避免热点问题,可能是更明智的选择。“为什么有时候选择异步复制而非同步复制反而能提高系统整体可用性?”

通过这种深度解读,我希望能将DDIA这本经典著作与当下工程实践紧密结合,帮助读者不仅理解"是什么",更能洞察"为什么"和"如何选择"。


二、本篇核心路线

本篇将详细介绍《数据密集型应用系统》第六章——数据分区

分而治之的艺术:如何将海量数据切分为多个分区,并在保证访问透明性的同时实现近乎线性的水平扩展


三、分区的基础

1. 为什么需要分区?

  1. 突破单机容量限制:横向扩展的基石

想象一下,如果你需要存储10PB的用户数据,即使最强大的服务器也无法容纳。通过分区,我们可以将这些数据分散到多台机器上,突破单机存储和处理能力的限制。

  1. 提高查询吞吐量:并行处理的威力

分区不仅解决存储问题,还能显著提升查询性能。当数据分布在多个节点上,查询可以并行处理,总吞吐量近乎与节点数成线性关系。

  1. 地理分布优化:靠近用户的数据

分区还允许我们根据地理位置分布数据,将用户数据部署在离用户最近的数据中心,显著降低访问延迟。

在这里插入图片描述

2. 分区面临的核心挑战

  1. 数据分布均衡:避免热点与倾斜

分区的首要挑战是如何均匀分配数据和查询负载。不平衡的分区可能导致某些节点成为热点,成为系统性能瓶颈。
如Kleppmann所述,看似合理的分区策略可能在实际工作负载下表现糟糕。例如,按用户名首字母分区看起来简单明了,但由于名字分布不均(某些首字母如"J"、"M"更常见),会导致严重的数据倾斜。

  1. 动态扩容的挑战:分区再平衡

随着数据量增长,系统需要添加更多节点。这就带来了分区再平衡问题:如何在最小化数据迁移的同时,重新分配分区以纳入新节点?
这个过程必须在保持系统持续可用的前提下进行,并避免在再平衡期间过度消耗网络和磁盘资源。Cassandra和HBase等系统采用不同策略解决这一挑战,各有优缺点。

  1. 请求路由:找到正确的分区

当数据分散在多个节点上,如何确保请求被发送到拥有所需数据的正确节点?这涉及到复杂的请求路由机制设计。
系统必须维护分区与节点的映射关系,这种映射可能存储在客户端、路由层或通过分布式协调服务管理。MongoDB的配置服务器和Kafka的控制器节点就是解决这类问题的不同方案。

3. 分区的基本术语与概念

在深入讨论分区策略前,让我们明确一些基本概念:

  1. 分区 vs 分片 vs 分割表
    这些术语在不同系统中可能指代相同概念。“分区”(Partition)是较通用的术语,而"分片"(Shard)常见于MongoDB,“分割表”(Split)则出现在Bigtable衍生系统中。

  2. 分区键
    决定记录应该被分配到哪个分区的字段。选择合适的分区键是系统设计的关键决策。

  3. 节点与分区的关系
    一个节点可以托管多个分区,而分区通常不会跨节点分割(虽然可能被复制到多个节点)。

  4. 再平衡
    重新分配分区到节点的过程,通常发生在添加/删除节点或负载不均衡时。

理解这些基本概念后,我们可以进一步探讨如何实际实现数据分区。


四、分区策略

1. 按键范围分区

1. 工作原理

范围分区是最直观的分区策略:按照主键的顺序范围将数据划分为多个分区,每个分区负责一段连续的键值范围,这种方式类似于百科全书按字母顺序分册或图书馆中的图书分类。

例如,如果我们有从a到z的键,可能会这样划分:

分区0:a到g
分区1:h到n
分区2:o到z

这些范围边界可以自动确定(如HBase中数据量达到一定阈值自动分裂),也可以由管理员手动指定(如MongoDB中的分片键范围)。

在这里插入图片描述

2. 优势与应用场景

范围分区的主要优点是支持高效的范围查询。当需要扫描按键排序的连续记录时,这些记录可能位于同一分区内,最小化了跨分区查询的需要。

当使用时间戳作为分区键时,范围分区允许高效访问特定时间范围内的数据。例如,在传感器数据系统中,查询"过去一小时的温度读数"只需访问负责最近时间范围的分区,而不必扫描全部数据。

Bigtable、HBase、RethinkDB和MongoDB都采用了这种分区策略,使它们特别适合时间序列数据处理和范围扫描操作。

3. 数据倾斜与热点问题

范围分区有一个关键缺点:容易产生热点。如果使用时间戳作为分区键,所有新写入操作都会定向到负责"最近"时间范围的分区,而其他分区则相对空闲。

例如在传感器网络中,如果使用测量时间戳作为主键,所有当前读数都会写入同一分区,导致该分区成为瓶颈,而系统整体吞吐量受限。

Kleppmann提到了一种缓解方法:在键中添加其他信息。例如,在时间戳前添加传感器名称,形如"sensor1:1234567890",这样写入负载就会分散到不同分区。

2. 按键哈希分区

1. 工作原理

哈希分区通过对记录的键应用哈希函数,然后根据哈希值分配到不同分区。如书中所述,基本公式为:

partition_number = hash(key) mod num_partitions

在这一过程中,哈希函数将不同范围的输入键映射到固定范围的哈希值,再通过取模操作确定分区。

在这里插入图片描述

2. 均衡分布与一致性哈希

哈希分区的主要优点是防止热点和数据倾斜。即使写入键有明显模式(如连续时间戳),哈希函数会将其均匀分散到各个分区。

然而,普通哈希分区面临着一个问题:当分区数量变化时(如添加新节点),大多数键需要重新映射,导致大量数据迁移。为解决这一问题,许多系统采用一致性哈希算法。

一致性哈希将哈希范围视为一个环,节点也被哈希到这个环上。每个键被分配到环上顺时针方向遇到的第一个节点。这样,当添加新节点时,只有该节点与其前任节点之间的键需要移动,大大减少了数据迁移量。

Cassandra和Voldemort等系统使用了这种机制(或其变体)来有效处理节点添加和删除。

3. 范围查询的限制

哈希分区的主要缺点,是失去了键顺序的优势,导致范围查询效率低下。当需要扫描一段连续的键时,系统必须向所有分区发送请求,因为相邻的键可能被哈希到不同分区。

这是一个根本性的权衡:要么优化键范围扫描(范围分区),要么优化负载均衡(哈希分区)。MongoDB允许开发者在创建分片集合时选择范围分区或哈希分区,反映了这种权衡。

3. 复合分区策略

1. 混合哈希-范围分区

为结合两种策略的优点,Kleppmann描述了复合分区方案。书中特别提到了Cassandra的复合主键方法:

主键的第一部分用于哈希分区
剩余部分用于在分区内排序

这使得查询可以在特定分区内进行范围扫描,同时保持跨分区的均衡分布。例如,书中解释了社交媒体应用可以使用用户ID进行哈希分区,然后在每个分区内按时间戳排序帖子,允许高效检索特定用户的时间范围内的帖子。

2. 分区键与主键解耦

另一种复合策略则是:完全分离分区键和主键。例如,MongoDB允许选择任何字段(或多个字段组合)作为分片键,这可能与文档的_id字段不同。

这种解耦增加了灵活性,允许开发者在不改变数据模型的情况下选择最优的分区方案。例如,即使用户ID是主键,系统也可以选择按地理位置分区,将相同地区的用户集中在同一分区,提高局部查询效率。

3. 应对倾斜的动态策略

针对数据和访问倾斜的问题,书中介绍了一些高级策略。例如,面对"明星"用户或热点项(拥有极高读写量的少数记录),系统可能需要特殊处理。

一些系统会检测到异常热点,并通过在不同分区上创建这些热点键的额外副本来分散负载。这样,读取请求可以分散到多个节点,缓解单节点瓶颈。

书中还讨论了当分区方案不可避免地导致某些分区比其他分区大得多时的处理方法。例如,一些系统允许将大分区进一步拆分成子分区,每个子分区可以独立分配给不同节点,实现更细粒度的负载均衡。


五、分区与二级索引

1. 按文档分区索引(本地索引)

1. 索引与分区共存的方式

主键分区相对简单,但现实应用中,我们经常需要按照非主键字段查询数据,这就需要二级索引。在分区数据库中,二级索引的处理变得特别复杂,因为索引本身也需要分区。

在文档分区索引方案中,每个分区独立维护仅覆盖该分区文档的二级索引,也被称为本地索引。这种方式下,索引与其索引的数据存储在同一分区。

例如,如果按用户ID分区存储用户资料,分区1可能包含ID为1-10000的用户数据及其本地索引(如按用户名、年龄建立的索引),分区2则包含ID为10001-20000的用户及其独立的本地索引,依此类推。

MongoDB、Cassandra和Elasticsearch都实现了这种本地索引模式。它使得写入操作简单高效,因为任何文档的更新只需在单个分区上更新相应的索引,不涉及跨分区通信。

在这里插入图片描述

2. 跨分区查询的挑战

本地索引的主要缺点在于查询复杂性。当通过二级索引查询时(如"找出所有30岁的用户"),客户端无法知道符合条件的数据分布在哪些分区,因此必须向所有分区发送查询请求,然后合并结果。这种查询模式被称为"分散/聚集"(scatter/gather)。

例如,在一个拥有100个分区的系统中,即使每个分区只需几毫秒就能响应,但查询延迟受限于最慢的分区响应。此外,查询需要处理的分区越多,某个分区出现故障或响应缓慢的概率就越高,导致整体查询可靠性下降。

这种扇出查询在大规模系统中可能导致严重的性能瓶颈,特别是当系统需要同时处理大量此类查询时。

3. 实践中的优化技巧

为减轻scatter/gather查询的影响,实际系统采用了多种优化技术:

分区剪枝:使用元数据或布隆过滤器等技术,在查询执行前判断哪些分区可能包含匹配文档,避免访问明显不包含目标数据的分区。

查询重写:自动分析查询条件,如果条件中包含分区键信息,则可以将全局查询转换为对特定分区的定向查询。

索引覆盖:设计包含查询所需所有字段的索引,使查询可以完全从索引中获取数据,无需访问实际文档。

结果限制与增量获取:对于大量结果的查询,实现分页或增量获取机制,避免一次传输过多数据。

即使有这些优化,对于大规模系统,本地索引在某些查询模式下的性能挑战依然存在,这促使了另一种索引方案的出现。

2. 按词条分区索引(全局索引)

1. 全局索引的结构设计

按词条分区索引(也称全局索引)采用与主数据不同的分区策略。在这种方法中,索引按照索引字段的值进行分区,而非按文档ID,这使得索引的分区方式可能与主数据完全不同。

全局索引的关键特性是索引覆盖了所有分区的数据。例如,如果主数据按用户ID哈希分区,但需要按照颜色字段创建索引,那么色彩索引可以这样分区:所有"红色"物品的索引项在一个分区,所有"蓝色"物品的索引项在另一个分区,不管这些物品的主数据存储在哪里。

这种方法的主要优势是可以高效执行针对索引字段的查询:为查找所有红色物品,系统只需访问"红色"索引分区,而不必扫描所有数据分区。这大大减少了跨分区查询的开销。

在这里插入图片描述

2. 写入放大与一致性挑战

全局索引的主要缺点是写入复杂性显著增加。当文档更新时,可能需要更新多个位于不同节点的索引分区。例如,如果一个产品从"红色"改为"蓝色",系统需要在红色索引分区删除旧索引项,并在蓝色索引分区添加新索引项。

更具挑战性的是,这种跨节点索引更新难以在单个原子事务中执行。在分布式系统中实现跨节点原子性需要复杂的两阶段提交协议,会显著增加写入延迟和系统复杂度。

因此,多数采用全局索引的系统选择了最终一致性模型:主数据的更新会立即生效,但索引更新可能会延迟。在这段时间窗口内,索引可能返回过时的结果。

全局索引的实现需要仔细考虑这种不一致窗口的大小和影响,以及如何向应用开发者清晰地传达这些限制。

3. 现代系统的平衡策略

面对本地索引和全局索引的权衡,现代数据库系统采用了多种战略:

混合索引架构:对不同查询模式的二级索引采用不同策略。频繁更新但查询简单的字段可能使用本地索引,而需要高效范围查询的字段则使用全局索引。

可配置一致性级别:允许应用根据业务需求为不同查询指定一致性要求,在性能和即时性间做出选择。

异步索引构建与修复:通过后台进程持续监控和纠正索引不一致,在保证最终一致性的同时优化写入性能。

变更数据捕获:使用单独的系统跟踪数据变更,用于异步更新索引,将写入开销与索引更新解耦。

这些策略的选择不仅取决于技术因素,还受到业务需求、查询模式和一致性要求的深刻影响。了解这些权衡对于设计大规模分布式数据系统至关重要。

3. 实践中的二级索引设计

1. Elasticsearch的分布式索引架构

Elasticsearch作为分布式搜索引擎的代表,其索引设计特别值得研究。它采用了基于Lucene的分布式架构,每个索引被分为多个分片(shards),每个分片本质上是一个独立的Lucene索引。

索引查询请求首先发送到协调节点,然后被并行转发到相关分片。每个分片执行本地搜索,协调节点收集并合并结果。这种架构能有效平衡写入和查询的需求,但对分片数量的规划尤为重要——过多分片会增加聚合开销,过少则限制了扩展能力。

Elasticsearch还采用了复制机制,每个分片可以有一个或多个副本,这不仅提高了数据可用性,还允许系统将读取请求分散到多个副本,提升查询吞吐量。

2. MongoDB与Cassandra的不同选择

MongoDB和Cassandra代表了不同的二级索引设计理念:

MongoDB主要支持本地索引,每个分片维护自己的索引数据。为优化跨分片查询,MongoDB引入了"覆盖查询"概念——如果索引包含查询所需的所有字段,系统可以完全从索引中获取数据而无需访问实际文档,显著提高查询性能。

Cassandra则同时提供本地二级索引和一种特殊形式的全局索引——物化视图。物化视图实质上是使用不同主键组织的同一数据副本,可以看作是预计算的查询结果。虽然占用额外存储空间,但物化视图为复杂查询提供了极高的性能。

3. 聚合索引与非聚合索引的权衡

在分区环境中,另一个重要的索引区分是聚合与非聚合策略:

聚合索引中,索引项与它们指向的记录物理上存储在一起。这种方法在传统B树索引中很常见,它减少了磁盘寻道,但更新成本较高,因为可能需要重组物理存储。

非聚合索引只存储指向记录的引用,索引与数据分离存储。这使得索引更新更轻量,但查询可能需要额外的数据获取操作,增加了I/O开销。

在分区数据库中,聚合索引通常只用于主键索引,而二级索引几乎都是非聚合的。这种选择反映了分布式环境中物理数据组织的复杂性和管理开销。

深入理解这些索引设计选择对于构建高性能、可扩展的数据系统至关重要。不同的查询模式和数据访问需求可能需要不同的索引策略,系统设计者必须在索引覆盖范围、维护成本和查询性能之间找到适合特定应用场景的平衡点。


六、分区再平衡

随着数据量增长或节点数量变化,系统需要重新分配分区,这个过程称为"再平衡"。再平衡是分区系统的关键操作,它直接影响系统可用性、性能和资源利用效率。一个优秀的再平衡机制应该满足以下目标:

  1. 平衡后负载均匀分布在各节点
  2. 再平衡期间系统持续可用
  3. 只移动必要的数据,最小化网络和磁盘开销
  4. 操作简单,尽可能自动化但可控

1. 再平衡策略分析

1. 固定分区数与动态分区

再平衡策略的第一个根本区别在于分区数量是固定还是动态调整。这种选择深刻影响了系统的可扩展性和管理复杂度。

固定分区数策略预先创建比节点数更多的分区,分区总数保持不变,再平衡只涉及调整分区到节点的映射。例如,一个有1000个分区和10个节点的系统中,每个节点平均负责100个分区。当增加到20个节点时,每个节点将负责50个分区,但分区总数仍然是1000个。

这种方法的优势在于实现相对简单,再平衡操作主要是分区迁移而非分区结构变更。然而,初始分区数的选择至关重要 - 过少会限制未来扩展,过多则增加管理开销。尤其是对异构集群,要实现负载均衡可能更加复杂,因为简单地将相同数量的分区分配给每个节点可能无法反映节点间的能力差异。

在这里插入图片描述

动态分区策略则根据数据大小自动调整分区数量。当分区增长超过配置阈值时,系统自动将其分裂为两个较小的分区;反之,当分区数据删除后变得过小,则可能与相邻分区合并。这种方法的复杂度更高,但提供了更大的灵活性。

动态分区的一个关键优势是它能自动适应数据分布的变化。例如,在时间序列数据中,随着新数据不断写入最新时间范围,系统会自动在该区域创建更多分区,而无需提前规划。这种"分裂热点"能力对于处理不可预测的数据增长模式尤为重要。

2. 自动与手动再平衡

再平衡触发机制是另一个关键设计决策,它直接影响系统的运维复杂度和稳定性。

全自动再平衡系统持续监控分区分布并自动进行调整。这种方法减少了人工干预,但自动决策算法可能难以适应复杂的运维需求。例如,系统可能无法识别临时性负载峰值与长期趋势的区别,从而触发不必要的再平衡。

更微妙的是,自动再平衡可能引发级联效应。当一个节点过载触发再平衡,其分区被转移到其他节点,可能导致这些节点也超过阈值,进而触发更多再平衡操作。在极端情况下,这种级联效应可能导致系统不稳定。

手动触发再平衡给予管理员更多控制权,允许根据更广泛的运维考虑来选择最佳时机。例如,管理员可能希望在网络流量较低时执行再平衡,或者协调多个系统的维护窗口。

实际系统通常采用混合策略:系统监控负载分布并提出再平衡建议,但需要人工确认执行。这种"半自动"方法结合了自动化的便利和人工判断的灵活性,特别适合关键生产环境。

3. 再平衡中的数据迁移

数据迁移是再平衡过程中最资源密集且风险最高的环节,不同系统采用了多种策略来优化这一过程。

整体迁移将整个分区作为一个单元迁移。这种方法概念简单,但可能导致瞬时网络和磁盘峰值负载。例如,迁移一个10GB的分区可能在短时间内占用大量带宽。

增量迁移将分区数据分批次转移,每批次可能只有几百MB。MongoDB和Elasticsearch采用这种方法,在迁移过程中,读写请求可能被转发到原分区,直至迁移完成。这减少了峰值资源需求,但延长了迁移时间窗口。

影子模式在目标位置创建分区副本,同时保持原分区可用。所有写入操作同时应用于两个位置,直到副本完全同步。这种方法最小化了迁移对可用性的影响,但增加了写入复杂度和临时存储需求。

多阶段迁移涉及复杂的协调过程:首先传输大部分历史数据;然后进入"追赶"阶段,传输迁移期间累积的变更;最后在极短的冻结窗口内传输剩余差异并切换流量。这种策略在最小化停机时间方面表现出色,但实现复杂度高。(实际上,在当下公司中迁移 pg 和 ck 采用的就是这样的方案)

每种迁移策略都涉及可用性、一致性、性能和复杂度之间的权衡。系统设计者需要根据具体应用场景和服务水平协议(SLA)选择合适的策略。

2. 请求路由机制

再平衡引发了一个关键问题:如何确保客户端请求被发送到当前持有所需数据的正确节点?这需要动态更新的路由机制,它有三种主要实现模式,每种都有独特的优缺点。

1. 客户端路由

在客户端路由模型中,客户端(或靠近客户端的服务)直接知道分区与节点的映射关系,因此可以直接联系合适的节点。

这种方法的核心挑战是保持客户端的分区映射表更新。实践中,客户端通常会定期查询某种服务发现机制,如ZooKeeper、etcd或consul,获取最新的分区分配信息。客户端本地缓存此映射,并在后台异步刷新,在发现路由错误时立即更新。

在这里插入图片描述

客户端路由的主要优势是减少网络跳转,客户端可以直接向目标节点发送请求,降低延迟。然而,这种模式增加了客户端复杂度,且在大规模部署时更新路由信息可能成为挑战,特别是当客户端数量庞大或分布广泛时。

2. 路由层代理

路由层代理模式中,客户端请求首先发送到独立的路由层,由路由层决定将请求转发给哪个数据节点。这种架构将路由逻辑从客户端剥离,大大简化了客户端实现。

设计高性能路由层需要注意几个关键点:路由层必须是无状态的,以便水平扩展;它应该维护高效的内存缓存,避免频繁查询元数据;还应实现智能批处理和请求合并,减少对数据节点的请求数量

MongoDB的mongos组件是路由层代理的典型实现。它从配置服务器获取分片映射,然后将客户端查询路由到适当的分片。这种设计使MongoDB客户端无需了解分片拓扑,显著简化了应用程序开发。

路由层的挑战在于它可能成为性能瓶颈或单点故障。实际系统通常部署多个路由节点,结合负载均衡器来分散请求并提供冗余。随着系统规模增长,路由层的扩展也变得至关重要。

3. 节点转发

节点转发模式允许客户端联系任意数据节点,如果该节点不包含所需数据,它会自动将请求转发到正确的节点。这种**“任意节点皆可入口”**的设计为客户端提供了极大便利。

这种模式的实现需要集群中的每个节点都知道整体分区分布情况,或者至少能够确定哪个节点负责特定请求。与客户端路由相比,节点转发的一个优势是集群内部通信通常更可靠且延迟更低。

然而,节点转发引入了额外的网络跳转,增加了查询延迟。在复杂查询场景下,这种影响可能更为显著 - 例如,如果查询需要从多个分区收集数据,转发模式可能导致复杂的请求链,增加了失败风险和性能波动。

Cassandra的读修复机制巧妙利用了节点转发模式。当协调节点发现某副本数据过时时,它不仅返回最新数据给客户端,还将更新转发给过时的副本,确保随时间推移系统达到一致状态。

在这里插入图片描述

3. 实践案例分析

1. HBase与HDFS的分层再平衡设计

HBase构建在HDFS之上,实现了一个多层次的分区与再平衡架构。HBase将表水平分割为"区域"(regions),每个区域包含一段连续的行范围,由一个RegionServer负责服务。

当区域增长超过配置阈值(默认10GB)时,HBase自动将其分裂为两个大小相近的子区域。这种基于大小的动态分裂策略适应了不规则的数据分布模式。例如,如果特定时间范围的数据突然增多,系统会在该范围创建更多区域,而不是简单地按照预定义边界划分。

HBase的再平衡涉及两个层面:RegionServer层面的区域迁移和HDFS层面的数据块再平衡。这种分层设计增加了复杂度,但提供了更精细的控制。例如,区域可以快速迁移(只需更新元数据),而实际数据可以稍后在HDFS层面异步平衡,减少对性能的即时影响。

HBase的Master服务器充当再平衡协调者,它监控区域分布并触发迁移操作。与完全自动系统不同,HBase允许管理员通过配置参数(如balancer.period和balancer.max.skew)控制再平衡的激进程度,在稳定性和均匀分布之间找到平衡点。

2. Cassandra的一致性哈希与虚拟节点

Cassandra的再平衡机制基于一致性哈希算法的改进版本,它引入了"虚拟节点"(vnodes)概念,显著提升了平衡性和弹性。

传统一致性哈希分配给每个物理节点哈希环上的一个位置,导致添加或删除节点时负载分布可能不均。Cassandra的创新在于每个物理节点负责多个小范围的哈希值(默认128个虚拟节点)。当新节点加入时,它从现有节点获取均匀分布的虚拟节点,而不是单一大范围。

例如,在一个3节点集群中添加第4个节点时,新节点从每个现有节点获取约32个虚拟节点(总共约128个vnodes的25%),而不是接管整个哈希环的25%。这种细粒度分配确保了更均匀的负载转移。

Cassandra的再平衡过程包含精心设计的步骤:首先,更新系统表记录新的令牌分配;然后每对节点协商数据传输;接着执行流式传输,使用压缩和校验和确保数据完整性;最后更新所有节点的路由信息。整个过程支持在线执行,无需停机。

虚拟节点的另一个显著优势是改进了故障恢复。当节点失效时,其负载分散到多个剩余节点,而不是单一后继者。这防止了任何单个节点在恢复过程中承受过重负担,提高了系统韧性。

3. Elasticsearch的分片分配与感知机制

Elasticsearch的再平衡称为"分片分配"(shard allocation),它融合了自动化与高度可配置性,支持复杂的部署场景和需求。

Elasticsearch使用固定数量的主分片和可变数量的副本分片。索引创建时确定主分片数,此后不可更改,但副本数可动态调整。这种设计在保持ID到分片映射稳定的同时,提供了扩展读查询能力的灵活性。

分片分配过程由集群中的主节点协调,它运行复杂的分配算法,考虑多种因素:

  1. 分片均衡:确保每个节点拥有相似数量的分片
  2. 索引均衡:避免单个索引的多个分片集中在同一节点
  3. 磁盘阈值:考虑节点磁盘使用率,防止任何节点接近容量上限
  4. 副本分离:确保主分片和副本分配到不同节点,增强容错性

Elasticsearch独特的"感知"机制允许管理员定义节点属性(如机架、区域或硬件规格),分配算法会考虑这些属性,确保关键分片在物理基础设施层面也实现了多样化。例如,通过机架感知,系统可以确保主分片和副本位于不同机架,防止机架级故障导致数据不可用。

在版本7.x中引入的"数据层"(data tiers)概念进一步扩展了Elasticsearch的再平衡能力,支持数据生命周期管理。热数据可分配在高性能节点,而冷数据自动迁移到成本更低的存储层,实现了性能和成本的优化平衡。

这些先进功能使Elasticsearch能适应从小型集群到跨区域、跨云部署的广泛场景,在保持可用性的同时优化资源利用率。


七、总结

在这一章中,我们深入探讨了数据分区这一分布式系统的基础支柱。我们不仅学习了按键范围分区和按键哈希分区这两种主流策略的工作原理,还剖析了它们各自的优缺点和适用场景。更重要的是,我们认识到分区不仅仅是简单的数据切分,而是一门平衡查询效率、负载均衡和可扩展性的艺术。


下节预告:
事务 —— 在分布式系统中实现可靠性与一致性的微妙艺术

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值