大家好,又见面了,我是你们的朋友全栈君。
CAP是分布式系统中的一个特别重要的理论。
CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。CAP是NOSQL数据库的基石。
分布式系统的CAP理论:理论首先把分布式系统中的三个特性进行了如下归纳:
关于分区容错性,需要解释一下:所谓分区指的是网络分区的意思,详细一点解释,比如你有A B两台服务器,它们之间是有通信的,突然,不知道为什么,它们之间的网络链接断掉了。那么现在本来AB在同一个网络现在发生了网络分区,变成了A所在的A网络和B所在的B网络。所谓的分区容忍性,就是说一个数据服务的多台服务器在发生了上述情况的时候,依然能继续提供服务。
如果不能满足p,就不能再网络分区的情况下保证服务,那么就是单个节点的情况,和分布式系统的设计初衷是背离的。所以讨论cap是,一般都是满足p的前提下,来选在满足a还是满足c。
如果满足C,即所有节点都需要有相同的数据,如果没有就不可服务,满足不了A;
如果满足A,即服务可以一直存在,那么在节点故障等场景下,就满足不了C。
BookKeeper是满足CP特性的分布式系统,并且同时提供了较高的可用性,下文会有论述。
BookKeeper是企业级的存储系统,提供强持久性、一致性和低延迟的保证。最初起源于Yahoo!,用来解决HDFS NN的单点问题。2011年作为ZooKeeper的子项目在Apache孵化,2015成为顶级项目。
一个企业级、实时的存储平台需要满足一下要求:
BK是一个可扩展、支持容错并且可以保证低延迟的存储服务。满足了以上的要求,适用于很多场景:
Records(Entry)
数据在BK以record的形式而非bytes的形式写入BK。Record是最小的I/O单位,也是地址单元。每个record都包含了一个序列号(单调递增的long性数)。Client从某一个record开始读取数据,或者tail一个序列,即监听添加到log的下一个record。可以单条或者批量接收record。序列号也可以用来随机检索record。
Entry除了包含写入bookie的实际数据之外,还包含一些元数据信息
字段 | 说明 | 类型 |
---|---|---|
Ledger Number | Entry写入的ledger ID | long |
Entry number | Entry的唯一ID | long |
Last confirmed (LC) | 最后记录的Entry ID | long |
Date | 数据 | byte[] |
Authentication code | 鉴权数据 which includes all other fields in the entry | byte[] |
Logs(BK的存储)
BK为提供了两种存储原语:
一个Ledger是数据records的序列,ledger在客户端显式的关闭或者writer写入失败时终止。一旦一个ledger 终止以后,就不能append记录。ledger是最底层的存储原语,可以用于存储有限的序列或者无限的流。
Ledgers是entry的序列,Entry是bytes序列。Entry写入ledger时,是
ledgers支持 append-only的语义。Entry写入到ledgers之后,就不可以被修改了。Client应用决定写入的顺序。
一个stream是无界的,无限的record序列。Leger可以被打开多次来append record。一个stream物理上会有多个ledger组成,每个ledger会根据时间或者空间滚动策略来滚动。stream会存在很长时间,所以保留数据策略是truncating,会根据时间或者空间策略来丢弃老的数据。
ledge和stream为历史数据或者实时数据提供了统一的存储抽象。Log streams提供了数据tail或者stream能力。实时数据存储在ledger中,随着时间会变成历史数据。
Log stream通常在namespacke下分类、管理。NameSpace是租户创建流时会使用到的一种机制。一个namespace是数据存储策略的部署和管理单元。在同一个namespace下的所有stream继承相同的配置,数据存储在存储策略配置的存储节点上。可以方便的管理过个stream。
BK在多个server之间备份存储数据entry,这些server成为Bookie。Bookie是个独立的Bk存储服务。
Bookies是处理ledgers(更具体一些,是ledger的片段)的server。Bookies作为ensemble的一部分起作用
一个bookie是一个独立的BookKeeper存储服务。出于性能的考虑,Bookie储存ledger的片段,而不是真个ledgers。对于任意的一个ledger, ensemble是存储这个ledger entries的bookies组。entry写入ledger时,会在ensemble中进行条带化的存储。
向ledger写入entry时,entry会被条带化到ensemble中(bookie的子集不是全部)。
BK的元数据主要是可用的BK以及ledger信息。使用ZK存储。
BookKeeper客户端主要有两个角色:创建和删除ledgers,向ledgers写入或者从ledgers中读取entry。
主要两种方式和BK交互:
BK提供了两种API:
选择哪种API,应该基于应用对于ledger的控制粒度。两种API可以在应用中共同使用。
注意:
前文中提到了一个实时储存系统需要满足的一些要求,总结如下:
Guarantee | Description |
---|---|
Replication | 数据在多台机器上都有持久存储的副本存在 |
Durability | 数据写入时,在ack之前需要写fsync到磁盘 |
Consistency | 提供 repeatable read的一致性 |
Avaliablity | 写:ensemble可以切换;读:提供了speculative read |
Low Latency | 读写I/O隔离 |
BK会为每个记录都复制并且存储几个独立的备份,通常是3或者5,备份可以位于相同或者不同数据中心。不同于其他的HA系统,使用了主/备复制或者流水线的算法来同步数据,BK使用 quorum-vote parallel replication algorithm,这种算法保证了可预测的数据复制延迟。下图描述了BK内部的数据复制:
上图中:
BK复制的核心思想:
这两个原则使得 BookKeeper replication 可以:
每个数据record写入BK时,都会保证被复制并且持久的存储在一定数量的bookie上。这种保证是通过显式的disk fsync和写入确认来实现的。
大多数的NoSQL类型的数据库,分布式文件系统以及消息系统(比如Kafka),都假设将数据复制到多个node的易失存储/内存就已经足够满足 best-effort 持久性要求,但是这些系统允许潜在的数据丢失。BK设计用来提供更强的持久性保证,可以完全避免数据丢失。
一致性保证是分布式系统的常见问题,尤其是在引入复制机制来提供持久性和可用性时。BK提供了一个简单但是强大的一致性保证——可重复读 :
可重复读一致性是通过LAC协议保证的。
在一个CAP理论体系中,BK是个CP系统。实际上,即使在硬件、网络或者其他失败的情况下,BK依旧提供了非常高的可用性。BK使用了一下的机制,为了保证读写的高可用性:
Availability type | Mechanism | Description |
---|---|---|
Write availability | Ensemble changes | 在bookkie出现失败时,client会重新配置写入的bookie。这就保证了在有足够的bookie时,写入总会成功。 |
Read availability | Speculative reads | This helps spread read traffic across bookies, which has the added benefit of reducing tail read latency. 和其他的系统只从指定的leader节点读数据不一样,BK允许客户端从任何ensemble的任何一个bookie读取record。这种机制可以将读取分散到多个bookie上,相比于其他系统,在降低及时消费的延迟方面有额外的优势。 |
强持久性和一致性保证是分布式系统中的复杂问题,尤其是要满足企业级的低延迟。BK可以完全满足这些要求:
最终,需要指出,显式的fsync带来的持久性、写入confirm以及重复读的一致性是状态处理的关键,尤其是对于流应用中的effectively-once处理。
可预测的低延迟对于实时应用来讲是非常重要的,尤其是一些关键性的在线商业服务以及数据库。以消息系统为例,在大多数的消息系统中,慢消费者会读取backlog,这就可能会造成性能降低,其原因是这些慢的消费者会读取辞旧存储介质,导致I/O抖动以及内存也的换近和换出。当写、tailing 读以及追赶读共享一个磁盘路径是,就会出现上述问题。
BK中bookie设计时为写入,tailing read以及catch-up read使用三个独立的I/O路径。写入和tailing read 要求可预测的延迟,catch-up read要求吞吐。为这几种workload提供物理隔离可以充分利用:
I/O隔离意味着BK可以提供多种优势并且不会妨碍其他优势。
构建在BK上的服务以segment ledger的形式存储log stream,这些segment会被复制到多个bookie。这样做带来了一系列的优势: 写入的高可用、负载均衡以及简化的操作体验。
首先,一个log stream的容量不会被限制到单个机器的存储容量,数据可以存储在整个集群上。
其次,扩展BK cluster时不需要log stream进行均衡。扩展BK只需要加入新的机器即可。新的bookie节点会被集群发现,然后立即写入可用。BK可以提供多种数据放置的策略,包括机架感知、区域感知以及基于权重等。
再次,有助于更快、更高效的失败恢复。当一个segment丢失(由于机器故障)或者崩溃(由于磁盘故障),BK可以确定需要修复哪一个segment(re-replicating entries to meet the replicas requirement),然后以多对多的形式并发修复。
相比于以分区为核心的系统(Kafka),BK的水平扩展具有优势的,log stream(类比于kafka的分区)顺序存储在一组机器上。扩展kafka时,需要负载均衡,这是一个资源密集、易错并且昂贵的操作。另外,partition-centric system的系统,单个机器故障或者磁盘故障都会导致复制全部的log stream。
Figure 1. Log stream: 所有的log segment都会被复制到配置数量的bookie’上(这里为3),并且数据可以跨一个配置的bookie上(这里为4). Log segment 均匀的分布,因此均有水平扩展的能力,并且不需要均衡。
作为一个实时的log stream存储,当负载增加或者愈来愈多的数据写入时,扩展性是十分重要的。BK的扩展性主要有一些因素决定:
Number of ledgers/streams
Stream的扩展性是指BK可以支撑大量的log stream, 几百到几百万个ledger,并且可以保证一致性。获取这种特性的关键是数据的存储格式。如果ledgers存储在独立的文件,在刷盘时,就会导致I/O分散在磁盘上。BK按照一种交替的格式存储数据,不同的ledger或者stream的数据会汇聚,然后存储在一个大文件中并且会做索引。这样降低了文件数量以及I/O操作,是的BK可以支撑大量的ledger或者stream。
Number of bookies
Bookie扩展性是通过增加bookie数量来支撑快速增长的流量。BK之中,bookie之间不会有直接的交互。这就允许BK通过增加机器就可以扩展集群,并且当超过了I/O带宽之后,也不需要数据均衡。这就允许BK集群在扩展时可以不用考虑数据的分布。Yahoo和Twitter的BK单个集群拥有10w+节点。
Number of clients
Client扩展性是指 log stream 存储支持大量并发client和大量扇出。通过以下要点实现:
应用程序可以通过增加stream数量或者bookie数量的方式来增加吞吐。另外,BK可以通过增加ensumble的大小来为单个stream增加吞吐。这对于那些需要保证数据顺序的有状态应用很有用。
BK设计为操作简单的。可以在系统运行时通过添加bookie的方式扩展容量。如果一个bookie不可用,其上的所有entries都被标记为under replicated,然后BK会自动从其他的可用的副本上将数据复制到新的bookie上。BK提供对于bookie的只读模式。在一些特定的场景下,bookie自动切换到只读模式,比如:磁盘满,磁盘损坏等。只读bookie不再接受写入,但是可以提供读服务。
另外,BK提供多种方式来管理集群:使用管理员 CLI 工具,使用 Java admin library 或者使用 HTTP REST API。REST API 可以用于实现外部工具。
BK提供了一个可插拔的鉴权机制。BK也可以配置支持多种鉴权机制。一个鉴权 provider 会创建客户端的identity,然后为client分配一个identifier(标识符),这个标识复用来决定client的那种操作是被授权的。BK模式提供两种鉴权机制:TLS和SASL(Kerberos)。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/219217.html原文链接:https://javaforall.cn