Cassandra 文档

版本

您正在查看预发布版本的文档。

查看最新版本

Dynamo

Apache Cassandra 依赖于 Amazon 的 Dynamo 分布式存储键值系统中的多种技术。Dynamo 系统中的每个节点都有三个主要组件

  • 对分区数据集的请求协调

  • 环形成员资格和故障检测

  • 本地持久性(存储)引擎

Cassandra 主要借鉴了前两个集群组件,同时使用基于日志结构化合并树 (LSM) 的存储引擎。特别是,Cassandra 依赖于 Dynamo 风格的

  • 使用一致性哈希进行数据集分区

  • 使用版本化数据和可调一致性进行多主复制

  • 通过八卦协议进行分布式集群成员资格和故障检测

  • 在商品硬件上进行增量扩展

Cassandra 的设计初衷是为了满足大规模 (PiB+) 的业务关键型存储需求。特别是,随着应用程序要求对 PB 级数据集进行完全全局复制,以及始终可用的低延迟读写,设计一种新的数据库模型变得势在必行,因为当时的传统关系数据库系统难以满足全球规模应用程序的新要求。

数据集分区:一致性哈希

Cassandra 通过使用哈希函数对系统中存储的所有数据进行 分区 来实现水平扩展。每个分区都复制到多个物理节点,通常跨越故障域,例如机架甚至数据中心。由于每个副本都可以独立地接受对它拥有的每个键的变异,因此每个键都必须进行版本化。与原始 Dynamo 论文中使用确定性版本和向量时钟来协调对键的并发更新不同,Cassandra 使用更简单的最后写入者获胜模型,其中每个变异都带有时间戳(包括删除),然后数据的最新版本就是“获胜”的值。从形式上讲,Cassandra 为每个 CQL 行使用最后写入者获胜元素集无冲突复制数据类型,或 LWW-Element-Set CRDT,来解决副本集上的冲突变异。

使用令牌环的一致性哈希

Cassandra 使用一种称为 一致性哈希 的特殊形式的哈希,将数据分区到存储节点上。在简单的哈希中,通常通过对键进行哈希并对桶的数量取模来将键分配到桶中。例如,如果您想使用简单的哈希将数据分布到 100 个节点,您可能会将每个节点分配到 0 到 100 之间的桶中,对输入键进行哈希并对 100 取模,并将数据存储在关联的桶中。但是,在这种简单的方案中,添加单个节点可能会使几乎所有映射失效。

Cassandra 而是将每个节点映射到令牌环上的一个或多个令牌,并通过对键进行哈希并沿着环“行走”来定义所有权,类似于 Chord 算法。一致性哈希与简单哈希的主要区别在于,当要哈希到的节点(桶)数量发生变化时,一致性哈希只需要移动一小部分键。

例如,如果我们有一个八节点集群,令牌均匀分布,复制因子 (RF) 为 3,那么要找到某个键的所有者节点,我们首先对该键进行哈希以生成令牌(这只是键的哈希值),然后我们沿着环顺时针“行走”,直到遇到三个不同的节点,此时我们找到了该键的所有副本。这个八节点集群的示例,gRF=3,可以可视化为如下

image

您可以看到,在类似 Dynamo 的系统中,键的范围,也称为 令牌范围,映射到相同的物理节点集。在这个示例中,所有落在令牌范围内的键,不包括令牌 1,包括令牌 2 (grange(t1, t2]),都存储在节点 2、3 和 4 上。

每个物理节点的多个令牌 (vnode)

如果要将数据分散到许多物理节点上,简单的单令牌一致性哈希效果很好,但如果令牌均匀分布,物理节点数量很少,则增量扩展(只添加几个容量节点)很困难,因为没有令牌选择可以用于新节点,这些节点可以使环保持平衡。Cassandra 试图避免令牌不平衡,因为不均匀的令牌范围会导致请求负载不均匀。例如,在前面的示例中,没有办法添加第九个令牌而不会导致不平衡;相反,我们必须在现有范围的中点插入 8 个令牌。

Dynamo 论文提倡使用“虚拟节点”来解决这个不平衡问题。虚拟节点通过将令牌环上的多个令牌分配给每个物理节点来解决问题。通过允许单个物理节点在环中占据多个位置,我们可以使小型集群看起来更大,因此即使添加单个物理节点,我们也可以使其看起来像添加了更多节点,从而在添加单个节点时,实际上从更多环邻居那里获取更多更小的数据块。

Cassandra 引入了一些术语来处理这些概念

  • 令牌:Dynamo 风格哈希环上的单个位置。

  • 端点:网络上的单个物理 IP 和端口。

  • 主机 ID:单个“物理”节点的唯一标识符,通常存在于一个 gEndpoint 上,包含一个或多个 gToken。

  • 虚拟节点(或 vnode):哈希环上的 gToken,由同一个物理节点拥有,即具有相同的 gHost ID。

令牌端点的映射产生了 令牌映射,Cassandra 在其中跟踪哪些环位置映射到哪些物理端点。例如,在下图中,我们可以使用四个物理节点来表示一个八节点集群,方法是将两个令牌分配给每个节点

image

每个物理节点的多个令牌提供了以下好处

  1. 添加新节点时,它会从环中的其他节点接收大约相同数量的数据,从而使数据在集群中的分布保持均匀。

  2. 当节点被停用时,它会大致均匀地将数据丢失给环中的其他成员,同样可以使数据在集群中的分布保持均匀。

  3. 如果节点不可用,查询负载(尤其是令牌感知查询负载)会在许多其他节点之间均匀分布。

但是,多个令牌也可能存在缺点

  1. 每个令牌都会在令牌环上引入最多 2 * (RF - 1) 个额外的邻居,这意味着存在更多节点故障组合,在这种组合中,我们会丢失令牌环一部分的可用性。令牌越多,出现故障的可能性就越高

  2. 集群范围的维护操作通常会变慢。例如,随着每个节点的令牌数量增加,集群必须执行的离散修复操作的数量也会增加。

  3. 跨令牌范围的操作的性能可能会受到影响。

请注意,在 Cassandra 2.x 中,唯一可用的令牌分配算法是选择随机令牌,这意味着为了保持平衡,每个节点的默认令牌数量必须非常高,为 256。这会导致许多物理端点耦合在一起,从而增加了出现故障的风险。这就是为什么在 3.x + 中添加了一个新的确定性令牌分配器,该分配器可以智能地选择令牌,以便在需要每个物理节点的更少令牌数量的同时,使环保持最佳平衡。

多主复制:版本化数据和可调一致性

Cassandra 将数据的每个分区复制到集群中的多个节点,以保持高可用性和持久性。当发生变异时,协调器会对分区键进行哈希运算,以确定数据所属的令牌范围,然后根据Replication Strategy将变异复制到该数据的副本。

所有复制策略都有一个复制因子RF)的概念,它指示 Cassandra 应该存在多少个分区副本。例如,对于RF=3的键空间,数据将写入三个不同的副本。副本始终选择为不同的物理节点,如果需要,可以跳过虚拟节点。复制策略还可以选择跳过存在于同一故障域(如机架或数据中心)中的节点,以便 Cassandra 集群可以容忍整个机架甚至数据中心节点的故障。

复制策略

Cassandra 支持可插拔的复制策略,它决定哪些物理节点充当给定令牌范围的副本。数据的每个键空间都有自己的复制策略。所有生产部署都应该使用NetworkTopologyStrategy,而SimpleStrategy复制策略仅适用于测试集群,在这些集群中您尚不知道集群的数据中心布局。

NetworkTopologyStrategy

NetworkTopologyStrategy 要求为集群中的每个数据中心指定一个复制因子。即使您的集群只使用一个数据中心,也建议使用NetworkTopologyStrategy而不是SimpleStrategy,以便在需要时更容易将新的物理或虚拟数据中心添加到集群中。

除了允许按数据中心单独指定复制因子外,NetworkTopologyStrategy还尝试从Snitch指定的不同机架中选择数据中心内的副本。如果机架数量大于或等于数据中心的复制因子,则保证每个副本都来自不同的机架。否则,每个机架将至少包含一个副本,但某些机架可能包含多个副本。请注意,这种机架感知行为有一些潜在的令人惊讶的影响。例如,如果每个机架中的节点数量不均匀,则最小机架上的数据负载可能高得多。同样,如果将单个节点引导到全新的机架中,它将被视为整个环的副本。出于这个原因,许多运营商选择将单个可用区或类似故障域中的所有节点配置为单个“机架”。

SimpleStrategy

SimpleStrategy 允许定义一个单个整数replication_factor。这决定了应该包含每行副本的节点数量。例如,如果replication_factor为 3,则三个不同的节点应该存储每行的副本。

SimpleStrategy 对所有节点进行相同处理,忽略任何配置的数据中心或机架。为了确定令牌范围的副本,Cassandra 从环中的令牌开始迭代,从感兴趣的令牌范围开始。对于每个令牌,它都会检查拥有节点是否已添加到副本集中,如果尚未添加,则将其添加到集中。此过程将持续进行,直到将replication_factor个不同的节点添加到副本集中。

瞬态复制

瞬态复制是 Cassandra 4.0 中的一个实验性功能,在原始 Dynamo 论文中不存在。此功能允许配置一个副本子集,这些副本仅复制尚未增量修复的数据。此配置将数据冗余与可用性分离。例如,如果您有一个在 RF=3 处复制的键空间,并将其更改为 RF=5,并具有两个瞬态副本,则您将从容忍一个失败的副本变为容忍两个副本,而不会相应地增加存储使用量。现在,三个节点将复制给定令牌范围的所有数据,而另外两个节点将仅复制尚未增量修复的数据。

要使用瞬态复制,首先在cassandra.yaml中启用该选项。启用后,SimpleStrategyNetworkTopologyStrategy都可以配置为瞬态复制数据。通过将复制因子指定为<total_replicas>/<transient_replicas来配置它。SimpleStrategyNetworkTopologyStrategy都支持配置瞬态复制。

瞬态复制的键空间仅支持使用read_repair设置为NONE创建的表;目前不支持单调读取。您也不能在 4.0 中使用LWT、记录批次或计数器。您可能永远无法在瞬态复制的键空间中使用物化视图,并且可能永远无法在其中使用二级索引。

瞬态复制是一个实验性功能,尚未准备好用于生产环境。目标受众是 Cassandra 的经验丰富的用户,他们能够完全验证其特定应用程序的部署。这意味着您拥有检查读取、写入、停用、删除、重建、修复和替换等操作是否符合您的查询、数据、配置、操作实践和可用性要求的经验。

4.next中预期的附加功能是支持使用瞬态复制进行单调读取,以及 LWT、记录批次和计数器。

数据版本控制

Cassandra 使用变异时间戳版本控制来保证数据的最终一致性。具体来说,所有进入系统的变异都带有时间戳,该时间戳要么来自客户端时钟,要么在没有客户端提供的时间戳的情况下,来自协调器节点的时钟。更新根据最后写入获胜的冲突解决规则进行解决。Cassandra 的正确性确实依赖于这些时钟,因此请确保正在运行适当的时间同步过程,例如 NTP。

Cassandra 将单独的变异时间戳应用于 CQL 分区中每行的每个列。行保证通过主键是唯一的,并且行中的每个列都根据最后写入获胜的冲突解决来解决并发变异。这意味着对分区内不同主键的更新实际上可以无冲突地解决!此外,CQL 集合类型(如映射和集合)使用相同的无冲突机制,这意味着对映射和集合的并发更新也保证可以解决。

副本同步

由于 Cassandra 中的副本可以独立接受变异,因此某些副本可能具有比其他副本更新的数据。Cassandra 具有许多尽力而为的技术来推动副本的收敛,包括读取路径中的Replica read repair <read-repair>和写入路径中的Hinted handoff <hints>

但是,这些技术只是尽力而为,为了保证最终一致性,Cassandra 实现anti-entropy repair <repair>,其中副本在其数据集上计算分层哈希树,称为Merkle 树,然后可以在副本之间进行比较以识别不匹配的数据。与原始 Dynamo 论文一样,Cassandra 支持完整修复,其中副本对其整个数据集进行哈希运算,创建 Merkle 树,将它们发送给彼此并同步任何不匹配的范围。

与原始 Dynamo 论文不同,Cassandra 还实现了子范围修复和增量修复。子范围修复允许 Cassandra 通过创建跨越部分数据范围的更多树来提高哈希树的分辨率(可能降至单个分区级别)。增量修复允许 Cassandra 仅修复自上次修复以来已更改的分区。

可调一致性

Cassandra 通过一致性级别支持在一致性和可用性之间进行每次操作的权衡。Cassandra 的一致性级别是 Dynamo 的R + W > N一致性机制的版本,其中运营商可以配置必须参与读取 (R) 和写入 (W) 的节点数量,以大于复制因子 (N)。在 Cassandra 中,您改为从常见一致性级别的菜单中选择,这允许运营商选择RW行为,而无需了解复制因子。通常,当读取一致性级别包含足够的节点以保证与写入一致性级别相交时,写入将对后续读取可见。

以下是一致性级别:

ONE

仅需要一个副本响应。

TWO

需要两个副本响应。

THREE

需要三个副本响应。

QUORUM

需要大多数(n/2 + 1)副本响应。

ALL

所有副本都需要响应。

LOCAL_QUORUM

需要本地数据中心(协调器所在的任何数据中心)中的大多数副本响应。

EACH_QUORUM

需要每个数据中心中的大多数副本响应。

LOCAL_ONE

仅需要一个副本响应。在多数据中心集群中,这也保证读取请求不会发送到远程数据中心的副本。

ANY

一个副本可以响应,或者协调器可以存储提示。如果存储了提示,协调器将在以后尝试重播提示并将变异传递给副本。此一致性级别仅适用于写入操作。

写入操作始终发送到所有副本,无论一致性级别如何。一致性级别仅控制协调器在响应客户端之前等待多少个响应。

对于读取操作,协调器通常只向满足一致性级别的足够副本发出读取命令。唯一的例外是,如果原始副本在指定的时间窗口内没有响应,则推测性重试可能会向额外的副本发出冗余读取请求。

选择一致性级别

通常,选择读写一致性级别,使副本集重叠,从而使所有已确认的写入对后续读取可见。这通常用 Dynamo 相同的术语表达,即 W + R > RF,其中 W 是写入一致性级别,R 是读取一致性级别,RF 是复制因子。例如,如果 RF = 3,则 QUORUM 请求将需要至少 2/3 个副本的响应。如果 QUORUM 用于写入和读取,则至少保证一个副本参与写入读取请求,这反过来保证了 quorum 将重叠,并且写入对读取可见。

在多数据中心环境中,LOCAL_QUORUM 可用于提供较弱但仍有用的保证:读取保证能看到同一数据中心内的最新写入。这通常就足够了,因为位于单个数据中心的客户端将读取自己的写入。

如果不需要这种强一致性,可以使用较低的一致性级别,如 LOCAL_ONEONE,以提高吞吐量、延迟和可用性。在跨多个数据中心的复制中,LOCAL_ONE 通常比 ONE 可用性低,但通常更快。实际上,如果任何数据中心中的单个副本可用,ONE 将成功。

分布式集群成员资格和故障检测

复制协议和数据集分区依赖于了解集群中哪些节点是存活的,哪些节点是死亡的,以便可以最佳地路由写入和读取操作。在 Cassandra 中,存活性信息通过基于八卦协议的故障检测机制以分布式方式共享。

八卦

八卦是 Cassandra 传播基本集群引导信息的方式,例如端点成员资格和节点间网络协议版本。在 Cassandra 的八卦系统中,节点不仅交换有关自身的信息,还交换有关它们知道的其他节点的信息。此信息使用 (generation, version) 元组的向量时钟进行版本控制,其中 generation 是单调时间戳,version 是每秒大约递增一次的逻辑时钟。这些逻辑时钟允许 Cassandra 八卦仅通过检查八卦消息中提供的逻辑时钟来忽略集群状态的旧版本。

Cassandra 集群中的每个节点都独立运行八卦任务,并定期运行。每秒,集群中的每个节点都会

  1. 更新本地节点的心跳状态(版本)并构建节点对集群八卦端点状态的本地视图。

  2. 在集群中选择另一个随机节点来交换八卦端点状态。

  3. 概率性地尝试与任何不可达节点进行八卦(如果存在)。

  4. 如果步骤 2 中没有发生,则与种子节点进行八卦。

当操作员首次引导 Cassandra 集群时,他们会将某些节点指定为种子节点。任何节点都可以是种子节点,种子节点和非种子节点之间的唯一区别是,种子节点被允许在没有看到任何其他种子节点的情况下引导到环中。此外,一旦集群引导完成,种子节点由于上述步骤 4 成为八卦的热点。

由于非种子节点必须能够联系至少一个种子节点才能引导到集群中,因此通常包含多个种子节点,通常每个机架或数据中心一个。种子节点通常使用现有的现成服务发现机制进行选择。

节点不必就种子节点达成一致,实际上,一旦集群引导完成,新启动的节点可以配置为使用任何现有节点作为种子。选择与种子节点相同的节点的唯一优势是它会增加它们作为八卦热点的有用性。

目前,八卦还传播令牌元数据和模式版本信息。此信息构成调度数据移动和模式拉取的控制平面。例如,如果节点在八卦状态中看到模式版本不匹配,它将与其他节点调度模式同步任务。由于令牌信息通过八卦传播,因此它也是用于教授节点哪些端点拥有哪些数据的控制平面。

环成员资格和故障检测

八卦构成环成员资格的基础,但故障检测器最终决定节点是UP还是DOWN。Cassandra 中的每个节点都运行 Phi 累积故障检测器 的变体,其中每个节点都在不断地独立决定其对等节点是否可用。此决定主要基于接收到的心跳状态。例如,如果节点在一段时间内没有看到来自节点的递增心跳,则故障检测器会“判决”该节点,此时 Cassandra 将停止将读取路由到该节点(写入通常将写入提示)。如果/当节点再次开始心跳时,Cassandra 将尝试联系并连接,如果它可以打开通信通道,它将标记该节点为可用。

UPDOWN 状态是本地节点的决定,不会与八卦一起传播。心跳状态会与八卦一起传播,但节点在能够通过实际网络通道成功地相互发送消息之前,不会将彼此视为UP

Cassandra 永远不会在没有操作员通过退役操作或使用 replace_address_first_boot 选项引导的新节点的明确指令的情况下从八卦状态中删除节点。这种选择是有意的,以允许 Cassandra 节点暂时失败,而不会导致数据不必要地重新平衡。这也有助于防止同时范围移动,其中多个令牌范围的副本同时移动,这可能会违反单调一致性,甚至会导致数据丢失。

商品硬件上的增量横向扩展

Cassandra 横向扩展以满足数据大小和请求速率增长需求。横向扩展意味着向环中添加更多节点,每个额外的节点都会带来计算和存储的线性改进。相比之下,纵向扩展意味着向现有数据库节点添加更多容量。Cassandra 也能够纵向扩展,在某些环境中,根据部署情况,它可能更可取。Cassandra 为操作员提供了选择横向扩展或纵向扩展的灵活性。

Cassandra 遵循 Dynamo 的一个关键方面是尝试在商品硬件上运行,并且许多工程选择是在这种假设下做出的。例如,Cassandra 假设节点可以随时失败,自动调整以充分利用可用的 CPU 和内存资源,并大量使用高级压缩和缓存技术,以从有限的内存和存储能力中获得最大的存储空间。

简单查询模型

Cassandra 与 Dynamo 一样,选择不提供 SQL 关系数据库管理系统 (RDBMS) 中常见的跨分区事务。这既为程序员提供了更简单的读写 API,又允许 Cassandra 更轻松地横向扩展,因为跨越多个节点的多分区事务在实现上非常困难,并且通常延迟很大。

相反,Cassandra 选择为单分区操作提供快速、一致、任何规模的延迟,允许检索整个分区或仅根据主键过滤器检索分区子集。此外,Cassandra 通过轻量级事务 CQL API 支持单分区比较和交换功能。

用于存储记录的简单接口

Cassandra 与 Dynamo 略有不同,它选择了一个比“简单键值”存储更复杂但比 SQL 关系数据模型简单得多的存储接口。Cassandra 提供了一个宽列存储接口,其中数据分区包含多行,每行包含一组灵活的单独类型列。每行由分区键和一个或多个聚类键唯一标识,并且每行可以拥有任意数量的列。

这允许用户在出现新需求时灵活地向现有数据集添加新列。模式更改仅涉及元数据更改,并且与实时工作负载完全并发运行。因此,用户可以安全地向现有 Cassandra 数据库添加列,同时确信查询性能不会下降。