在本章中,我们将研究Cassandra架构的几个方面,以了解它如何完成其工作。 我们将解释集群的拓扑结构,以及节点如何在对等设计中进行交互,以使用诸如八卦,反熵和暗示切换等技术来维护集群的健康状况并交换数据。 查看节点的设计,我们检查Cassandra用于支持读取,写入和删除数据的架构技术,并检查这些选择如何影响架构考虑因素,如可伸缩性,持久性,可用性,可管理性等。 我们还讨论了Cassandra采用分阶段事件驱动架构,该架构充当请求委派的平台。

在我们介绍这些主题时,我们还提供了在Cassandra源代码中可以找到其实现的位置的参考。

关键词
Keyword Explain
Gossip 点对点通信协议,用以Cassandra集群中节点间交换位置和状态
Partitioner 决定如何在集群中的节点间分发数据,即在哪个节点防止数据的第一个replica
Repliac placement strategy 决定在哪些节点放置每行数据的其他replica。Cassandra在集群中的多个节点存储数据的多份拷贝,replicas确保可靠和容错
Snitch 定义了复制策略用来放置replicas和路由请求所使用的拓扑信息
Virtual nodes 虚拟节点,指定数据与物理节点的所属关系
Token Ring 令牌环

数据中心和机架

Cassandra经常用于跨越物理上分开的位置的系统中。 Cassandra提供两个级别的分组,用于描述集群的拓扑:数据中心和机架。 机架是彼此非常接近的逻辑节点集,可能在单个机架设备中的物理机器上。 数据中心是一组逻辑机架,可能位于同一建筑物内,并由可靠的网络连接。 具有多个数据中心和机架的示例拓扑如图6-1所示。

开箱即用,Cassandra带有一个包含单个机架(“RAC1”)的单个数据中心(“DC1”)的默认配置。 我们将在第7章中学习如何构建更大的集群并定义其拓扑。

Cassandra利用您提供的有关群集拓扑的信息来确定数据的存储位置以及如何有效地路由查询。 Cassandra尝试在多个数据中心存储数据副本,以最大限度地提高可用性和分区容差,同时更愿意将查询路由到本地数据中心的节点,以最大限度地提高性能。

Gossip和故障

检测为了支持分散和分区容错,Cassandra使用一种Gossip协议,允许每个节点跟踪有关群集中其他节点的状态信息。 Gossip在计时器上每秒运行一次。

Gossip协议(有时称为“流行病协议”)通常假设网络故障,通常用于非常大的分散式网络系统,并且通常用作分布式数据库中复制的自动机制。 他们的名字取自人类八卦的概念,这是一种沟通形式,同伴可以选择与谁交换信息。

“Gossip协议”的起源

术语“Gossip协议”最初是由施乐公司帕洛阿尔托研究中心的研究员艾伦·德默斯于1987年创造的,他研究了通过不可靠网络路由信息的方法。

Cassandra中的Gossip协议主要由org.apache.cassandra.gms.Gossiper类实现,该类负责管理本地节点的八卦。 当服务器节点启动时,它会将自己注册到gossiper以接收端点状态信息。

由于Cassandra的Gossip用于故障检测,Gossiper类维护一个活着和死亡的节点列表。

这就是gossiper的工作原理:

  • 每秒一次,gossiper将在集群中选择一个随机节点并用它初始化一个八卦会话。 每一轮八卦都需要三条消息。

  • 八卦发起者向其选择的朋友发送GossipDigestSynMessage。

  • 当朋友收到此消息时,它返回GossipDigestAckMessage。

  • 当发起者从朋友收到ack消息时,它向朋友发送GossipDigestAck2Message以完成一轮八卦。

当gossiper确定另一个端点已经死亡时,它会通过在其本地列表中将其标记为死并且记录该事实来“定罪”该端点。

Cassandra对故障检测提供强大的支持,正如流行的分布式计算算法Phi Accrual Failure Detection所指定的那样。 这种故障检测方式起源于2004年日本高等科学技术研究所。

应计故障检测基于两个主要思想。 第一个一般的想法是故障检测应该是灵活的,这是通过将其与被监控的应用程序分离来实现的。 第二个也是更新颖的想法对传统故障检测器的概念提出了挑战,传统故障检测器由简单的“心跳”实现,并根据是否接收到心跳来判断节点是死还是死。 但是,应计失败检测决定了这种方法是天真的,并且在死亡和活着的极端之间找到了一个位置 - 一个怀疑水平

因此,故障监视系统输出关于节点失败的可信度的连续“怀疑”水平。 这是可取的,因为它可以考虑网络环境中的波动。 例如,仅仅因为一个连接被捕获并不一定意味着整个节点已经死亡。 因此,与简单的二元评估相反,基于解释(心跳采样),怀疑提供了更加流畅和主动的指示更弱或更强的失败可能性。

Phi阈值和应计故障检测器

应计故障检测器输出与每个进程(或节点)关联的值。 该值称为Phi。 在面对易变的网络条件时,该值以从头开始设计为自适应的方式输出,因此不是简单地检查服务器是启动还是关闭的二进制条件。

配置中的Phi囚犯阈值调整故障检测器的灵敏度。 较低的值会增加灵敏度,较高的值会降低灵敏度,但不会以线性方式。

Phi值指的是服务器可能关闭的怀疑程度。 使用AFD的Cassandra等应用程序可以为它们发出的Phi值指定可变条件。 Cassandra通常可以使用此机制在大约10秒内检测到故障节点。

您可以阅读Naohiro Hayashibara等人的原始Phi Accrual Failure Detection文件。 在http://www.jaist.ac.jp/~defago/files/pdf/IS_RR_2004_010.pdf。

故障检测在Cassandra中由org.apache.cassandra.gms实现.WORailDetector类实现了org.apache.cassandra.gms.IFailureDetector接口。 它们共同允许运营,包括:

  • IsAlive(InetAddress类)

    探测器将报告给定节点的存活情况。

  • interpret(InetAddress)

    由gossiper用来帮助它根据通过计算Phi达到的怀疑水平来决定节点是否存活(如Hayashibara论文中所述)。

  • report(InetAddress)

    当节点收到心跳时,它会调用此方法。

snitch

snitch的工作是确定集群中每个节点的相对主机接近度,用于确定要读取和写入的节点。 Snitches收集有关您的网络拓扑的信息,以便Cassandra可以有效地路由请求。 金色飞贼将弄清楚节点与其他节点的关系。

作为一个例子,让我们来看看snitch如何参与读取操作。 当Cassandra执行读取时,它必须联系由一致性级别确定的多个副本。 为了支持最大的读取速度,Cassandra选择单个副本来查询完整对象,并询问其他副本的哈希值,以确保返回所请求数据的最新版本。 snitch的作用是帮助识别将返回最快的副本,这是查询完整数据的副本。

默认的小报(SimpleSnitch)是拓扑不知道的;也就是说,它不了解群集中的机架和数据中心,这使得它不适合多数据中心部署。出于这个原因,Cassandra为不同的云环境提供了几个小故障,包括Amazon EC2,Google Cloud和Apache Cloudstack。

snitch可以在org.apache.cassandra.locator包中找到。每个snitch都实现了IEndpointSnitch接口。我们将在第7章学习如何为您的环境选择和配置适当的小报。

虽然Cassandra提供了一种静态描述群集拓扑的可插拔方式,但它还提供了一种称为动态窃听的功能,可帮助优化读写路由。这是它的工作原理。您选择的小报将被另一个名为DynamicEndpointSnitch的小报包裹。动态探测器从选定的探测器中获得对拓扑的基本了解。然后,它监视对其他节点的请求的性能,甚至跟踪哪些节点正在执行压缩。性能数据用于为每个查询选择最佳副本。这使Cassandra能够避免将请求路由到执行不佳的副本。

动态窃听实现使用八卦使用的Phi故障检测机制的修改版本。 “不良阈值”是一个可配置参数,用于确定首选节点必须执行的性能比最佳性能节点要差多少才能失去其优先级状态。 每个节点的分数会定期重置,以便让性能较差的节点证明它已恢复并恢复其首选状态。

Rings and Tokens

到目前为止,我们一直关注Cassandra如何跟踪集群中节点的物理布局。 让我们转变一下,看看Cassandra如何在这些节点之间分配数据。

Cassandra将群集管理的数据表示为环。 环中的每个节点被分配由令牌描述的一个或多个数据范围,该令牌确定其在环中的位置。 令牌是用于标识每个分区的64位整数ID。 这为令牌提供了从-263到263-1的可能范围。

节点声明小于或等于每个标记的值范围的所有权,并且大于前一节点的标记。 具有最低令牌的节点拥有小于或等于其令牌的范围以及大于最高令牌的范围,其也被称为“包裹范围”。这样,令牌指定完整的环。 图6-2显示了一个概念环布局,包括单个数据中心中的节点。 该特定布置被构造成使得连续的令牌范围分布在不同机架中的节点上。

通过使用散列函数为分区键计算令牌,将数据分配给节点。 将该分区密钥令牌与各个节点的令牌值进行比较,以识别拥有该数据的范围,从而识别该节点。

令牌范围由org.apache.cassandra.dht.Range类表示。

虚拟节点

早期版本的Cassandra以相当静态的方式为每个节点分配了一个令牌,要求您为每个节点计算令牌。 虽然有一些工具可用于根据给定数量的节点计算令牌,但仍然是为cassandra.yaml文件中的每个节点配置initial_token属性的手动过程。 这也使得添加或替换节点成为一项昂贵的操作,因为重新平衡集群需要移动大量数据。

Cassandra的1.2版本引入了虚拟节点的概念,简称vnodes。 不是将单个令牌分配给节点,而是将令牌范围分解为多个较小的范围。 然后为每个物理节点分配多个令牌。 默认情况下,将为每个节点分配256个这些令牌,这意味着它包含256个虚拟节点。 默认情况下,自2.0起已启用虚拟节点。

Vnodes可以更轻松地维护包含异构机器的集群。 对于群集中具有更多可用计算资源的节点,可以通过在cassandra.yaml文件中设置num_tokens属性来增加vnode的数量。 相反,您可以将num_tokens设置为较低,以减少功能较弱的计算机的vnode数量。

Cassandra自动处理群集中每个节点的令牌范围计算,与num_tokens值成比例。 vnodes的令牌分配由org.apache.cassandra.dht.tokenallocator.ReplicationAwareTokenAllocator类计算。

虚拟节点的另一个优点是它们可以加速一些更重量级的Cassandra操作,例如引导新节点,停用节点和修复节点。 这是因为与多个较小范围上的操作相关联的负载在集群中的节点上更均匀地分布。

Partitioners

分区程序确定数据在群集中的节点之间的分布方式。正如我们在第5章中学到的,Cassandra将数据存储在宽行或“分区”中。每行都有一个用于标识分区的分区键。然后,分区器是用于计算分区键的令牌的散列函数。根据分区密钥令牌的值,每行数据在环内分布。

Cassandra在org.apache.cassandra.dht包中提供了几个不同的分区器(DHT代表“分布式哈希表”)。 Murmur3Partitioner在1.2中添加,从那时起一直是默认的分区器;它是由Austin Appleby开发的murmur算法的高效Java实现。它生成64位哈希。以前的默认值是RandomPartitioner。

由于Cassandra通常可插拔设计,您还可以通过实现org.apache.cassandra.dht.IPartitioner类并将其放在Cassandra的类路径上来创建自己的分区程序。

复制策略

节点用作不同数据范围的副本。如果一个节点发生故障,其他副本可以响应对该数据范围的查询。 Cassandra以对用户透明的方式跨节点复制数据,复制因子是群集中将接收相同数据的副本(副本)的节点数。如果复制因子为3,则环中的三个节点将具有每行的副本。

第一个副本将始终是声明令牌落入范围的节点,但副本的其余部分根据复制策略(有时也称为副本放置策略)放置。

为了确定副本放置,Cassandra实现了Gang of Four Strategy模式,该模式在公共抽象类org.apache.cassandra.locator.AbstractReplicationStrategy中概述,允许算法的不同实现(用于完成相同工作的不同策略)。每个算法实现都封装在一个扩展AbstractReplicationStrategy的类中。

开箱即用,Cassandra提供了这个接口的两个主要实现(抽象类的扩展):SimpleStrategy和NetworkTopologyStrategy。 SimpleStrategy将副本放置在环的连续节点上,从分区器指示的节点开始。 NetworkTopologyStrategy允许您为每个数据中心指定不同的复制因子。在数据中心内,它将副本分配给不同的机架,以便最大限度地提高可用性。

传统复制策略

第三种策略OldNetworkTopologyStrategy用于向后兼容。它之前被称为RackAware Strategy,而SimpleStrategy以前称为RackUnawareStrategy。 NetworkTopologyStrategy以前称为DataCenterShardStrategy。这些变化在0.7版本中有效。

该策略是针对每个键空间独立设置的,并且是创建键空间的必需选项,如第5章所述。

一致性水平

在第2章中,我们讨论了Brewer的CAP定理,其中一致性,可用性和分区容差相互折衷。 Cassandra提供可调整的一致性级别,允许您在细粒度级别上进行这些权衡。您可以在每个读取或写入查询上指定一致性级别,以指示您需要多少一致性。更高的一致性级别意味着更多节点需要响应读取或写入查询,从而更加确保每个副本上存在的值相同。

对于读取查询,一致性级别指定在返回数据之前必须响应读取请求的副本节点数。对于写入操作,一致性级别指定必须响应多少副本节点才能将写入报告为客户端成功。由于Cassandra最终是一致的,因此对其他副本节点的更新可能会在后台继续。

可用的一致性级别包括ONE,TWO和THREE,每个级别指定必须响应请求的副本节点的绝对数量。 QUORUM一致性级别需要来自大多数副本节点的响应(有时表示为“复制因子/ 2 + 1”)。 ALL一致性级别需要来自所有副本的响应。我们将在第9章中更详细地研究这些一致性级别和其他级别。

对于读取和写入,ANY,ONE,TWO和THREE的一致性级别被认为是弱的,而QUORUM和ALL被认为是强的。 Cassandra中的一致性是可调的,因为客户端可以在读取和写入时指定所需的一致性级别。有一个方程式通常用于表示在Cassandra中实现强一致性的方法:R + W> N =强一致性。在该等式中,R,W和N分别是只读副本计数,写副本计数和复制因子;所有客户端读取都将在此方案中看到最近的写入,并且您将具有很强的一致性。

区分一致性水平和复制因素

如果您是Cassandra的新手,复制因素有时会与一致性级别混淆。每个键空间设置复制因子。客户端按查询指定一致性级别。复制因子指示在每次写入操作期间要用于存储值的节点数。一致性级别指定客户端已决定必须响应的节点数,以便对成功的读取或写入操作有信心。由于一致性级别基于复制因子而不是系统中的节点数,因此产生混淆。

查询和协调器节点

让我们将这些概念结合起来讨论Cassandra节点如何交互以支持来自客户端应用程序的读取和写入。图6-3显示了与Cassandra交互的典型路径。

客户端可以连接到群集中的任何节点以启动读取或写入查询。此节点称为协调节点。协调器识别哪些节点是正在写入或读取的数据的副本,并将查询转发给它们。

对于写入,协调器节点联系所有副本(由一致性级别和复制因子确定),并且当与一致性级别相称的多个副本确认写入时认为写入成功。

对于读取,协调器联系足够的副本以确保满足所需的一致性级别,并将数据返回给客户端。

当然,这些是Cassandra如何运作的“快乐路径”描述。我们将很快讨论一些Cassandra的高可用性机制,包括暗示切换。

Memtables,SSTables和Commit Logs

现在让我们来看看Cassandra的一些内部数据结构和文件,如图6-4所示。 Cassandra将数据存储在内存和磁盘上,以提供高性能和耐用性。在本节中,我们将重点介绍Cassandra对称为memtables,SSTables和提交日志的构造的使用,以支持从表中写入和读取数据。

执行写入操作时,会立即将其写入提交日志。提交日志是一种崩溃恢复机制,支持Cassandra的持久性目标。在写入提交日志之前,写入不会算作成功,以确保如果写入操作没有进入内存存储(记忆,稍后讨论),它仍然可以恢复数据。如果关闭数据库或意外崩溃,则提交日志可确保数据不会丢失。那是因为下次启动节点时,会重放提交日志。实际上,这是唯一一次读取提交日志;客户从不读它。

在将其写入提交日志后,该值将写入名为memtable的内存驻留数据结构。每个memtable包含特定表的数据。在Cassandra的早期实现中,memtables存储在JVM堆上,但从2.1版开始的改进已将大部分可记录数据移动到本机内存。这使得Cassandra不易受到Java垃圾收集导致的性能波动的影响。

当存储在memtable中的对象数达到阈值时,memtable的内容将被刷新到名为SSTable的文件中的磁盘。然后创建一个新的memtable。这种冲洗是一种非阻塞操作;单个表可能存在多个memtables,一个当前,其余等待刷新。它们通常不必等待很长时间,因为节点应该非常快速地冲洗它们,除非它过载。

每个提交日志都维护一个内部位标志,以指示是否需要刷新。首次收到写操作时,会将其写入提交日志,并将其位标志设置为1.每个表只有一个位标志,因为只有一个提交日志在整个服务器上写入。对所有表的所有写入都将进入相同的提交日志,因此位标志指示特定提交日志是否包含尚未针对特定表刷新的任何内容。将memtable正确刷新到磁盘后,相应的提交日志的位标志将设置为0,表示提交日志不再需要为了持久性目的而维护该数据。与常规日志文件一样,提交日志具有可配置的翻转阈值,一旦达到此文件大小阈值,日志将翻转,随身携带任何现存的脏位标志。

SSTable是从Google的Bigtable借来的概念。一旦memtable作为SSTable刷新到磁盘,它就是不可变的,并且不能被应用程序更改。尽管SSTables已经被压缩,但这种压缩只会改变它们的盘上表示;它基本上执行mergesort的“合并”步骤到新文件中,并在成功时删除旧文件。

他们为什么被称为“SSTables”?

“SSTable”是“Sorted String Table”压缩的想法对于Cassandra来说有点不准确,因为数据不是作为字符串存储在磁盘上。

自1.0版本发布以来,Cassandra一直支持SSTables的压缩,以便最大限度地利用可用存储。此压缩可按表配置。每个SSTable还有一个关联的Bloom过滤器,用作额外的性能增强器(请参阅“Bloom过滤器”)。

所有写入都是顺序的,这是写入在Cassandra中表现良好的主要原因。向Cassandra写入值时不需要任何读取或搜索,因为所有写入都是追加操作。这对性能提出了一个关键的限制,即磁盘的速度。压缩旨在分摊数据的重组,但它使用顺序I / O来执行此操作。因此,分裂可以获得性能优势;写操作只是一个立即附加,然后压缩有助于组织以获得更好的未来读取性能。如果卡桑德拉天真地插入他们最终所属的价值观,那么写客户就会为前方的搜寻付出代价。

在读取时,Cassandra将读取SSTable和memtables以查找数据值,因为memtable可能包含尚未刷新到磁盘的值。 Memtables由org.apache.cassandra.db.Memtable类实现。

高速缓存

正如我们在图6-4中看到的,Cassandra提供了三种形式的缓存:

  • 密钥缓存将分区键映射存储到行索引条目,有助于更快地读取存储在磁盘上的SSTable。密钥缓存存储在JVM堆上。

  • 行缓存缓存整行,并且可以大大加快频繁访问的行的读访问,但代价是更多的内存使用。行缓存存储在堆外内存中。

  • 计数器缓存在2.1版本中添加,通过减少最常访问的计数器的锁争用来提高计数器性能。

默认情况下,启用密钥和计数器缓存,同时禁用行缓存,因为它需要更多内存。 Cassandra会定期将其缓存保存到磁盘,以便在节点重启时更快地加热它们。我们将在第12章中研究如何调整这些缓存。

暗示切换

请考虑以下情形:向Cassandra发送写入请求,但由于网络分区,硬件故障或其他原因,写入正确所属的副本节点不可用。为了确保在这种情况下环的普遍可用性,Cassandra实现了一种称为提示切换的功能。您可能会想到一个提示作为包含写请求信息的便利贴。如果写入所属的副本节点发生故障,协调器将创建一个提示,这是一个小提示,上面写着“我有写入信息用于节点B.我将挂起这个写入,并且当节点B重新上线时我会注意到;当它发生时,我会向它发送写请求。“也就是说,一旦它通过八卦检测到节点B重新上线,节点A将”切换“到节点B关于写入的”提示“。 Cassandra为每个要写入的分区保留一个单独的提示。

这允许Cassandra始终可用于写入,并且通常使群集能够维持相同的写入负载,即使某些节点已关闭。它还可以减少故障节点重新联机后出现故障的时间。

通常,出于一致性级别的目的,提示不计为写入。一致性水平ANY例外,增加了0.6。这种一致性水平意味着单独的暗示切换将被认为足以实现写入操作的成功。也就是说,即使只能记录一个提示,写入仍然算作成功。请注意,写入被认为是持久的,但在将提示传递到目标副本之前,数据可能无法读取。

暗示切换和保证交付

暗示切换在亚马逊的Dynamo中使用,对于那些了解消息传递系统(例如Java消息服务(JMS))中保证传递概念的人来说是熟悉的。在持久的保证传递JMS队列中,如果无法将消息传递给接收方,JMS将等待给定的间隔,然后重新发送请求,直到收到消息。

暗示切换存在实际问题(并且保证传送方法就此而言):如果节点离线一段时间,则提示可以在其他节点上大量建立。然后,当其他节点注意到故障节点已经重新联机时,它们倾向于在请求时充满该节点,就在它最容易受到攻击的时候(当它在失败后努力重新开始时)。为解决此问题,Cassandra将提示的存储限制为可配置的时间窗口。也可以完全禁用提示切换。

顾名思义,org.apache.cassandra.db.HintedHandOffManager是在内部管理提示切换的类。

虽然暗示切换有助于提高Cassandra的可用性,但它并不能完全取代手动修复的需要以确保一致性。

轻量级事务和Paxos

正如我们在第2章中讨论的那样,Cassandra提供可调整的一致性,包括通过指定足够高的一致性级别来实现强一致性的能力。但是,在客户端需要读取然后写入数据的情况下,强一致性不足以防止竞争条件。

为了帮助解释这个例子,让我们重新审视第5章中的my_keyspace.user表。想象一下,我们正在构建一个想要管理用户记录的客户端,作为帐户管理应用程序的一部分。在创建新用户帐户时,我们希望确保用户记录尚不存在,以免我们无意中覆盖现有用户数据。因此,我们先读取以查看记录是否存在,然后仅在记录不存在时才执行创建。

我们正在寻找的行为称为线性化一致性,这意味着我们希望保证其他客户端不会在我们的读取和写入查询之间进行自己的修改。自2.0发布以来,Cassandra支持轻量级事务(或“LWT”)机制,提供可线性化的一致性。

Cassandra的LWT实施基于Paxos。 Paxos是一种共识算法,允许分布式对等节点就提议达成一致,而无需主设备协调事务。 Paxos和其他一致性算法成为传统的基于两阶段提交的分布式事务方法的替代方案(参考“两阶段提交问题中的两阶段提交”的注释)。

基本的Paxos算法包括两个阶段:准备/承诺和建议/接受。为了修改数据,协调器节点可以向副本节点提出新值,承担领导者的角色。其他节点可以同时充当领导者以进行其他修改。每个副本节点都会检查提案,如果提案是最新的,它承诺不接受与任何先前提案相关的提案。每个副本节点还返回它仍在进行中的最后一个提案。如果提案得到大多数复制品的批准,领导者会提交提案,但需要注意的是,提案必须首先提交在其提案之前的任何正在进行的提案。

Cassandra实现扩展了基本的Paxos算法,以支持所需的read-before-write语义(也称为“check-and-set”),并允许在事务之间重置状态。它通过在算法中插入两个附加阶段来实现这一点,因此它的工作方式如下:

  • 准备/无极
  • 读/结果
  • 建议/接受
  • 提交/ ACK

因此,成功的事务需要协调器节点和副本之间的四次往返。 这比普通写入更昂贵,这就是为什么在使用LWT之前你应该仔细考虑你的用例。

更多关于Paxos

已经写了几篇关于Paxos协议的论文。 其中一个最好的解释是Leslie Lamport的“Paxos Made Simple”。

Cassandra的轻量级事务仅限于单个分区。 在内部,Cassandra为每个分区存储Paxos状态。 这可确保不同分区上的事务不会相互干扰。

您可以在org.apache.cassandra.service.paxos包中找到Cassandra的Paxos算法实现。 StorageService利用这些类,我们将很快了解这些类。

墓碑

在关系世界中,您可能已经习惯了“软删除”的概念。应用程序将发出一个更新语句,用于更改名为“已删除”的列中的值,而不是实际执行删除SQL语句。例如,有时这样做是为了支持审计跟踪。

卡桑德拉有一个类似的概念叫做墓碑。这是所有删除工作的方式,因此会自动为您处理。执行删除操作时,不会立即删除数据。相反,它被视为一个更新操作,在逻辑上放置一个墓碑。逻辑删除是一种删除标记,在压缩可以运行之前,需要在SSTable中抑制旧数据。

有一个相关的设置叫做Garbage Collection Grace Seconds。这是服务器等待垃圾收集墓碑的时间。默认情况下,它设置为864,000秒,相当于10天。 Cassandra跟踪墓碑时代,一旦墓碑比GCGraceSeconds更旧,它将被垃圾收集。此延迟的目的是为节点提供无法恢复的时间;如果某个节点的停机时间长于此值,则将其视为失败并替换。

Bloom过滤器

Bloom过滤器用于提高读取性能。他们以他们的发明者Burton Bloom命名。布隆过滤器是非常快速,非确定性的算法,用于测试元素是否是集合的成员。它们是非确定性的,因为它可能从Bloom过滤器获得误报读取,但不是假阴性。 Bloom过滤器的工作方式是将数据集中的值映射到位数组,并使用散列函数将较大的数据集压缩为摘要字符串。根据定义,摘要使用的内存量比原始数据少得多。过滤器存储在内存中,用于通过减少对密钥查找的磁盘访问需求来提高性能。磁盘访问通常比内存访问慢得多。因此,在某种程度上,Bloom过滤器是一种特殊的缓存。执行查询时,首先检查Bloom过滤器,然后再访问磁盘。因为假阴性是不可能的,如果过滤器指示该元素在该集合中不存在,那肯定不会;但是如果过滤器认为元素在集合中,则访问磁盘以确保。

Bloom过滤器由org.apache.cassandra.utils.BloomFilter类实现。 Cassandra提供了通过增加过滤器大小来增加布隆过滤器精度(减少误报数)的能力,但代价是更多内存。这种误报机会可以按表调整。

Bloom过滤器的其他用途

Bloom过滤器用于其他分布式数据库和缓存技术,包括Apache Hadoop,Google Bigtable和Squid Proxy Cache。

Compaction

正如我们已经讨论过的,SSTables是不可变的,这有助于Cassandra实现如此高的写入速度。但是,为了支持快速读取性能和清​​除过时数据值,定期压缩这些SSTable非常重要。执行Cassandra中的压缩操作以合并SSTable。在压缩期间,合并SSTable中的数据:合并键,组合列,丢弃逻辑删除,并创建新索引。

压缩是通过合并大量累积数据文件来释放空间的过程。这大致类似于在关系世界中重建表格。但Cassandra的主要区别在于它旨在作为一个透明的操作,在服务器的整个生命周期中摊销。

在压缩时,对合并的数据进行排序,在排序的数据上创建新索引,并将新合并,排序和索引的数据写入单个新的SSTable(每个SSTable由多个文件组成,包括:Data,Index和过滤)。此过程由类org.apache.cassandra.db.compaction.CompactionManager管理。

压缩的另一个重要功能是通过减少所需的搜索次数来提高性能。需要检查有限数量的SSTable以查找给定密钥的列数据。如果密钥经常发生变异,那么突变很可能都会在刷新的SSTable中结束。压缩它们可以防止数据库执行搜索以从每个SSTable中提取数据,以便找到读取请求中请求的每个列的当前值。

执行压缩时,磁盘I / O和磁盘上的数据大小会出现临时峰值,同时读取旧的SSTable并写入新的SSTable。

Cassandra通过策略模式支持多种压缩算法。压缩策略是为每个表设置的选项。压缩策略扩展了AbstractCompactionStrategy类。可用的策略包括:

  • SizeTieredCompactionStrategy(STCS)是默认的压缩策略,建议用于写入密集型表
  • 对于读密集型表,建议使用LeveledCompactionStrategy(LCS)
  • DateTieredCompactionStrategy(DTCS),用于时间序列或其他基于日期的数据。

我们将在第12章重新讨论这些策略,以讨论为每个表选择最佳策略。

压实的一个有趣特征涉及其与增量修复的交集。 2.1中增加了一个名为反压缩的功能。顾名思义,反压缩与常规压缩有些相反,其结果是将SSTable划分为两个SSTable,一个包含修复数据,另一个包含未修复数据。

权衡是压缩策略引入了更多的复杂性,压缩策略必须单独处理已修复和未修复的SSTable,以便它们不会合并在一起。

主要Compaction怎么样?

具有先前经验的用户可能会记得,Cassandra公开了一种称为主压缩(也称为完全压缩)的管理操作,该操作将多个SSTable合并为单个SSTable。虽然此功能仍然可用,但执行主要压缩的效用随着时间的推移已大大降低。实际上,在生产环境中实际上不鼓励使用,因为它往往会限制Cassandra删除陈旧数据的能力。我们将在第11章中通过nodetool了解有关SSTables的此操作和其他管理操作的更多信息。

反熵,修复和Merkle树

Cassandra使用反熵协议,这是一种用于修复复制数据的八卦协议。反熵协议通过比较数据的副本和协调副本之间观察到的差异来工作。亚马逊的Dynamo中使用了反熵,而Cassandra的实现则以此为模型(参见Dynamo论文的第4.7节)。

Cassandra反熵

在Cassandra中,反熵一词通常用于两个稍微不同的上下文中,其含义有一些重叠:

该术语通常用作副本同步机制的简写,以确保不同节点上的数据更新到最新版本。
在其他时候,Cassandra被描述为具有反熵能力,包括复制同步以及暗示切换,这是我们在“暗示切换”中读到的写入时间反熵机制。

通过称为读修复和反熵修复的两种不同模式支持副本同步。读取修复是指在读取数据时同步副本。 Cassandra从多个副本中读取数据以实现请求的一致性级别,并检测是否有任何副本具有过期值。如果节点数量不足具有最新值,则立即执行读取修复以更新过期副本。否则,可以在读取返回后在后台执行修复。 Cassandra以及Project Voldemort和Riak等直接键/值存储可以观察到这种设计。

反熵修复(有时称为手动修复)是在节点上执行的手动启动的操作,作为常规维护过程的一部分。这种类型的修复是使用名为nodetool的工具执行的,我们将在第11章中了解。运行nodetool修复会导致Cassandra执行主要压缩(请参阅“压缩”)。在主要压缩期间,服务器启动TreeRequest / TreeReponse对话以与相邻节点交换Merkle树。 Merkle树是表示该表中数据的哈希。如果来自不同节点的树不匹配,则必须对它们进行协调(或“修复”)以确定它们应该被设置为的最新数据值。此树比较验证是org.apache.cassandra.service.AbstractReadExecutor类的责任。

什么是Merkle树?

Merkle树以其发明者Ralph Merkle命名,也称为“哈希树”。它是一种表示为二叉树的数据结构,它很有用,因为它以较短的形式汇总了较大数据集中的数据。在哈希树中,叶子是要汇总的数据块(通常是文件系统上的文件)。树中的每个父节点都是其直接子节点的哈希,它紧密地压缩摘要。

在Cassandra中,Merkle树在org.apache.cassandra.utils .MerkleTree类中实现。

Cassandra中使用Merkle树来确保节点的对等网络接收未经改变且未受损害的数据块。它们还用于加密以验证文件和传输的内容。

Cassandra和Dynamo都使用Merkle树进行反熵,但它们的实现有点不同。在Cassandra,每张桌子都有自己的Merkle树;树在主要压缩期间创建为快照,并且只在将其发送到环上的相邻节点所需的时间内保留。此实现的优点是它减少了网络I / O.

分阶段事件驱动架构(SEDA)

Cassandra的设计受到分阶段事件驱动架构(SEDA)的影响。 SEDA是一种高度并发的互联网服务的通用架构,最初是由Matt Welsh,David Culler和Eric Brewer在2001年的一篇名为“SEDA:用于良好条件,可扩展的互联网服务的架构”的论文中提出的(你可能会从我们的讨论中回忆起来) CAP定理)。您可以在http://www.eecs.harvard.edu/~mdw/proj/seda上阅读原始的SEDA论文。

在典型的应用中,单个工作单元通常在单个线程的范围内执行。例如,写操作将在同一线程内开始和结束。然而,Cassandra是不同的:它的并发模型基于SEDA,因此单个操作可以从一个线程开始,然后将该工作交给另一个线程,该线程可以将其移交给其他线程。但这不是由当前线程将工作交给另一个线程。相反,工作被细分为所谓的阶段,与阶段关联的线程池(实际上是java.util.concurrent.ExecutorService)决定执行。

阶段是一个基本的工作单元,单个操作可以在内部状态 - 从一个阶段过渡到下一个阶段。因为每个阶段都可以由不同的线程池处理,所以Cassandra可以获得巨大的性能提升。此设计还意味着Cassandra能够更好地在内部管理自己的资源,因为不同的操作可能需要磁盘I / O,或者它们可能受CPU限制,或者它们可能是网络操作,等等,因此池可以管理它们根据这些资源的可用性开展工作。

阶段包括传入事件队列,事件处理程序和关联的线程池。阶段由控制器管理,控制器确定调度和线程分配; Cassandra使用线程池java.util.concurrent.ExecutorService实现这种并发模型。要具体了解其工作原理,请查看org.apache.cassandra.concurrent.StageManager类。以下操作在Cassandra中表示为阶段,包括我们在本章中讨论的许多概念:

  • 读(本地读)
  • Mutation (本地写作)
  • Gossip
  • 反熵(nodetool修复)
  • Read repair
  • 迁移(进行架构更改)
  • Hinted handoff

您可以使用nodetool tpstats命令观察与每个阶段关联的线程池,我们将在第10章中了解该命令。
还有一些额外的操作也被实现为阶段,例如对memtables的操作,包括将数据刷新到SSTable并释放内存。这些阶段实现IVerbHandler接口以支持给定动词的功能。因为变异的概念被表示为一个阶段,它可以在插入和删除操作中发挥作用。

SEDA的实用主义方法

随着时间的推移,Cassandra和基于SEDA架构文章的其他技术的开发人员遇到了性能问题,因为每个阶段和每个阶段之间的事件队列需要单独的线程池,即使对于短期阶段也是如此。 Matt Welsh在后续博客文章“SEDA的回顾”中承认了这些挑战。

随着时间的推移,Cassandra的开发人员放宽了严格的SEDA约定,将一些阶段折叠到同一个线程池中以提高吞吐量。但是,在代码中仍然存在将工作分成阶段并使用队列和线程池来管理这些阶段的基本原则。

Managers and Services

有一组类形成了Cassandra的基本内部控制机制。我们在本章中已经遇到过一些,包括Hinted HandOffManager,CompactionManager和StageManager。我们将在这里简要介绍一些其他类,以便您熟悉一些更重要的类。其中许多通过Java Management Extension(JMX)公开MBean以报告状态和指标,并且在某些情况下允许配置和控制其活动。我们将在第10章中学习更多关于与这些MBean交互的知识。

Cassandra守护进程

org.apache.cassandra.service.CassandraDaemon接口表示在单个节点上运行的Cassandra服务的生命周期。它包括您可能期望的典型生命周期操作:启动,停止,激活,停用和销毁。

您还可以使用类org.apache.cassandra.service.EmbeddedCassandraService以编程方式创建内存中的Cassandra实例。使用Cassandra创建嵌入式实例对于单元测试程序非常有用。

存储引擎

Cassandra的核心数据存储功能通常被称为存储引擎,它主要由org.apache.cassandra.db包中的类组成。主要入口点是ColumnFamilyStore类,它管理表存储的所有方面,包括提交日志,memtables,SSTable和索引。

存储引擎的主要更改

存储引擎在3.0版本中进行了大量重写,以使Cassandra的内存和磁盘表示与CQL保持一致。 CASSANDRA-8099 JIRA问题提供了对这些变化的精彩总结。

存储引擎重写是许多其他更改的前提,最重要的是,支持物化视图,这是在CASSANDRA-6477下实现的。如果您想要更好地理解“引擎盖下”所需的更改以启用这些强大的新功能,这两个JIRA问题可以提供有趣的阅读。

存储服务

Cassandra使用org.apache.cassandra.service.StorageService类表示的服务包装存储引擎。存储服务包含节点的令牌,该令牌是指示节点负责的数据范围的标记。

存储代理

org.apache.cassandra.service.StorageProxy位于StorageService前面,用于处理响应客户端请求的工作。它与其他节点协调以存储和检索数据,包括在需要时存储提示。 StorageProxy还有助于管理轻量级事务处理。

直接调用存储代理

虽然可以以编程方式调用StorageProxy,但作为内存中实例,请注意,这不被视为Cassandra的官方支持的API,因此在版本之间进行了更改。

消息服务

org.apache.cassandra.net.MessagingService的目的是为消息交换创建套接字侦听器;来自此节点的入站和出站邮件都来自此服务。 MessagingService.listen方法创建一个线程。然后,每个传入连接都使用org.apache.cassandra.net.IncomingTcpConnection(一个扩展Thread的类)进入ExecutorService线程池,以反序列化消息。验证消息,然后路由到适当的处理程序。

因为MessagingService也大量使用了阶段,并且它维护的池用MBean包装,所以你可以通过JMX找到很多关于这个服务是如何工作的(读取是否得到备份等)。

流管理器

Streaming是Cassandra通过持久TCP连接将SSTable文件部分从一个节点发送到另一个节点的优化方式;节点之间的所有其他通信都通过序列化消息发生。 org.apache.cassandra.streaming.Stream Manager处理这些流消息,包括连接管理,消息压缩,进度跟踪和统计信息。

CQL本地传输服务器

CQL Native Protocol是客户端用来与Cassandra通信的二进制协议。 org.apache.cassandra.transport包中包含实现此协议的类,包括Server。 此本机传输服务器管理客户端连接并路由传入请求,将执行查询的工作委派给StorageProxy。

还有其他几个类来管理Cassandra的主要功能。 以下是一些调查您是否感兴趣的内容:

Key feature Class
Repair org.apache.cassandra.service.ActiveRepairService
Caching org.apache.cassandra.service.CachingService
Migration org.apache.cassandra.service.MigrationManager
Materialized views org.apache.cassandra.db.view.MaterializedViewManager
Secondary indexes org.apache.cassandra.db.index.SecondaryIndexManager
Authorization org.apache.cassandra.auth.CassandraRoleManager

系统Keyspaces

在真正的“dogfooding”风格中,Cassandra利用自己的存储来跟踪有关集群和本地节点的元数据。 这类似于Microsoft SQL Server维护元数据库master和tempdb的方式。 主服务器用于保存有关磁盘空间,使用情况,系统设置和常规服务器安装说明的信息; tempdb用作工作空间来存储中间结果并执行常规任务。 Oracle数据库始终有一个名为SYSTEM的表空间,用于类似目的。 Cassandra系统密钥空间的使用方式与此类似。

让我们回到cqlsh,快速浏览一下Cassandra系统密钥空间中的表:

cqlsh> DESCRIBE TABLES;Keyspace system_traces
----------------------
events sessionsKeyspace system_schema
----------------------
materialized_views  functions  aggregates  types            columns
tables              triggers   keyspaces   dropped_columnsKeyspace system_auth
--------------------
resource_role_permissons_index  role_permissions  role_members
rolesKeyspace system
---------------
available_ranges                       sstable_activity    local
range_xfers                            peer_events         hints
materialized_views_builds_in_progress  paxos
"IndexInfo"                            batchlog
peers                                  size_estimates
built_materialized_views               compaction_historyKeyspace system_distributed
---------------------------
repair_history  parent_repair_history
看到不同的系统Keyspaces?

如果您在2.2之前使用的是Cassandra版本,则可能看不到列出的某些键空间。虽然基本系统密钥空间从一开始就存在,但是在1.2中添加了system_traces密钥空间以支持请求跟踪。 2.2中添加了system_auth和system_distributed键空间,以分别支持基于角色的访问控制(RBAC)和修复数据的持久性。最后,与模式定义相关的表从3.0系统迁移到system_schema密钥空间。

查看这些表,我们发现其中许多与本章讨论的概念有关:

  • 有关通过八卦传达的集群结构的信息存储在system.local和system.peers中。这些表包含有关本地节点和群集中其他节点的信息,包括IP地址,按数据中心和机架的位置,CQL和协议版本。

  • system.range_xfers和system.available_ranges跟踪由每个节点管理的令牌范围以及需要分配的任何范围。

  • system_schema.keyspaces,system_schema.tables和system_ schema.columns存储为集群定义的键空间,表和索引的定义。

  • 在系统.specialized_ views_builds_in_progress和system.built_materialized_views表中跟踪物化视图的构造,从而产生system_schema.materialized_views中可用的视图。

  • 用户提供的扩展,例如用于用户定义类型的system_schema.types,用于按表配置的触发器的system_schema.triggers,用于用户定义函数的system_schema。用户定义聚合的system_schema.aggregates。

  • system.paxos表存储正在进行的事务的状态,而system.batchlog表存储原子批次的状态。

  • system.size_estimates存储每个表的估计分区数,用于Hadoop集成。

删除system.hints表

传统上,暗示的切换存储在system.hints表中。 正如有思想的开发人员所指出的那样,提示实际上是要保留短时间并删除的消息意味着这种用法实际上是使用Cassandra作为队列的众所周知的反模式的实例,我们在第5章中讨论过 。提示存储已移至3.0版本中的平面文件。

让我们回到cqlsh,快速浏览一下Cassandra系统键空间的属性:

cqlsh> USE system;cqlsh:system> DESCRIBE KEYSPACE;CREATE KEYSPACE system WITH replication = {'class': 'LocalStrategy'} AND durable_writes = true;...

我们在这里截断了输出,因为它列出了每个表的完整结构。查看输出中的第一个语句,我们看到系统键空间正在使用复制策略LocalStrategy,这意味着此信息旨在供内部使用,而不是复制到其他节点。

系统Keyspace的不变性

描述系统键空间产生类似于描述任何其他键空间的输出,因为表是使用CREATE TABLE命令语法描述的。这可能有些误导,因为您无法修改系统键空间的架构。

总结

在本章中,我们研究了Cassandra架构的主要支柱,包括gossip,snitches,分区,复制,一致性,反熵,暗示切换和轻量级事务,以及如何使用分阶段事件驱动架构最大化性能。我们还研究了一些Cassandra的内部数据结构,包括memtables,SSTables和提交日志,以及它如何执行各种操作,如逻辑删除和压缩。最后,我们调查了一些主要的类和接口,指出了您想深入了解代码库的关键点。

第六章: Cassandra架构--Cassandra:The Definitive Guide 2nd Edition相关推荐

  1. 第十四章:Cassandra部署与整合--Cassandra:The Definitive Guide 2nd Edition

    在这个,我们的最后一章,是时候分享一些最后的建议,因为你正在努力在生产中部署Cassandra.我们将讨论在规划部署时要考虑的选项,并探讨在各种云环境中部署Cassandra的选项.我们将关注一些与C ...

  2. 2022版Maven教程 - 第六章 单一架构案例

    2022版Maven教程 - 第六章 单一架构案例 一.创建工程,引入依赖 1.架构 ①架构的概念 ②单一架构 2.创建工程 3.引入依赖 ①搜索依赖信息的网站 [1]到哪儿找? [2]怎么选择? ② ...

  3. 《Hadoop.The.Definitive.Guide.4th.Edition.2015.3》学习笔记

    一.读后感 最近读完了<Hadoop.The.Definitive.Guide.4th.Edition.2015.3>英文第4版,个人感觉这本书是hadoop目前最权威.最全面.最靠谱的书 ...

  4. 第六章 设计程序架构 之 设计实现WebSocket策略

    1. 概述 传统网页的通信方式是请求-响应模式,每次请求-响应都是新的连接.连接的建立和断开也是需要消耗资源的. WebSocket是基于TCP协议,实现单个连接上的双向通信. 本章内容包括: 异步读 ...

  5. ASP.NET Web程序设计 第六章 三层架构

    一.三层架构的理解 1.三层架构:基于高内聚低耦合的软件架构原则,提高项目的可维护性.可扩展性. 2.分层: 1)数据访问层:实现与数据库(数据存储区)的数据交互. 2)业务逻辑层:实现业务规则及数据 ...

  6. Hadoop: The Definitive Guide (3rd Edition)

    chapter 1 解决计算能力不足的问题,不是去制造更大的计算机,而是用更多的计算机来解决问题. 我们生活在一个数据的时代."大数据"的到来不仅仅是影响到那些科研和金融机构,对小 ...

  7. 走向ASP.NET架构设计-第六章-服务层设计(中篇)

    走向ASP.NET架构设计-第六章-服务层设计(中篇) 前言:上一篇文章介绍了一些服务层的基本知识,而且也简要的介绍了SOA的有关知识,本篇主要是介绍在服务层可以采用的一些模式.  本篇议题如下: F ...

  8. 系统架构师学习笔记_第六章(下)_连载

    系统架构师学习笔记_第六章(下)_连载 6.3 基于 UML 的软件开发过程 6.3.1  开发过程概述 UML 是独立于软件开发过程的,能够在几乎任何一种软件开发过程中使用.迭代的渐进式软件开发过程 ...

  9. NoSQL应用场景及Cassandra架构实现分析

    大家好!我是陈国庆,今天是圣诞节,首先祝大家节日快乐.今天我主要讲两个方面的内容,一是NoSQL的应用场景,另一个是Cassandra架构实现分析.作为一个NoSQL非常接触的应用,我们分析它的架构, ...

最新文章

  1. Fragment 和 FragmentActivity的使用
  2. Nestjs 微服务
  3. 在Jetson Xavier NX上安装pycuda报错:src/cpp/cuda.hpp:14:10: fatal error: cuda.h: No such file or directory
  4. Windows下本机简易监控系统搭建(Telegraf+Influxdb+Grafana)--转
  5. 用jquery实现html5的placeholder功能
  6. iOS 钥匙串的基本使用
  7. 利用arcgis将execl数据可视化(点)
  8. 大型网站的架构设计问题—-大型高并发高负载网站的系
  9. [转]解读2014之前端篇:任性的颠覆式改变
  10. 引人注目的意思是什么,怎么用引人注目造句,引人注目的近义词有哪些?
  11. Web/前端性能优化
  12. socket pro 启动客户端的命令
  13. 激光导航技术是如何实现的?
  14. 控制服务器系统,控制服务器操作系统
  15. IC之无毛刺时钟切换
  16. java毕业设计旅游分享系统源码+lw文档+mybatis+系统+mysql数据库+调试
  17. 百度快照排名推广如何优化?
  18. LeetCode 314 周赛
  19. “大众系列 Volkswagen”被美国GBC代理,附处理方案
  20. windows照片查看器提示无法显示图片内存不足

热门文章

  1. 使用qrcode.js生成网址二维码
  2. 都雪冬 20190919-2 功能测试
  3. 发现今年是阿里和腾讯这么多年来最大规模的校招
  4. 理解:虚拟内存,虚拟内存地址,物理内存,物理内存地址
  5. 【Android实现16位灰度图数据转RGB数据并以bitmap格式显示】
  6. Mac OS X 工具技巧:一文搞定个性化命令行窗口(iTerm2 + zsh + oh-my-zsh 配置Powerline风格)
  7. 汇编语言常用DOS功能调用示例
  8. numpy Week2.2
  9. iOS开发支付 — 内购(IAP)
  10. “操作必须使用一个可更新的查询”故障解决