IoTDB 首创并应用的共识协议统一框架,为用户提供了灵活选择不同共识算法的可能性。
对于一个分布式集群而言,为了使得海量数据场景下集群能够横向扩展,集群需要按照一定的规则将全部数据分成多个子集存储在不同的节点上,从而能够更加充分地利用到集群中各个节点的存算资源。对于集群中的任何一个分片而言,为了满足高可用的需求,需要将数据在多个物理节点上冗余存储多个副本,进而避免单点故障的出现。
由于同一份数据有多个副本,可能会出现不同副本间数据不一致的现象。现有的分布式系统一般通过共识算法来处理多个副本间的数据一致性问题。
然而,不同的业务场景对共识算法的一致性、可用性等要求不同。比如对于应用监控场景,Google 的时序数据库 Monarch 就明确指出对于监控场景的时序数据其会更关注可用性而不是一致性。不过对于数据分区等关键元信息,其依然使用了保证外部一致性的 Spanner 数据库进行管理。
对于专为物联网场景而生的分布式时序数据库 IoTDB 而言,针对时序数据,往往可以根据场景选择可用性更好的共识算法;而针对分区信息和元数据等核心信息,往往可以选择一致性较强的共识算法。
本文将从共识算法讲起,深入解析 IoTDB 对于共识算法一致性、可用性和性能等方面的选择与取舍,并介绍 IoTDB 独有的共识算法框架,帮助您理解并掌握 IoTDB 的多种共识策略,从而根据您的业务需要对共识算法进行灵活的选择与配置。
01
共识算法
(1)为什么需要共识算法
前面一期(分布式架构三部曲(二))提到,分布式系统通过将数据进行分片,并在多个物理节点冗余存储为多个副本的横向扩展方式来提升系统整体的可用性和性能。IoTDB 采取横向扩展的分布式架构,如下图所示。
尽管分布式系统的多副本设计大大增加了系统的可用性和性能,但这种横向扩展的思路也给系统带来了挑战,例如:
不同副本如何保证数据是一致的?
不同副本同时接收不同的写入,以谁为准?
一个节点挂掉后,如何在其他节点恢复上面的副本?
查询哪个副本的数据会更好?
共识算法即为解决上述挑战而设计的机制。让对外的用户既能享受到高可用和性能,也感知不到多副本的存在。
(2)共识算法的概念与分类
共识算法是分布式系统中的一种关键机制,用于在多个参与节点之间达成一致决策,即使在存在网络延迟、节点故障或恶意攻击的情况下,也能确保系统的一致性和可靠性。在分布式系统中,各个节点的副本可能存在不一致状态,而共识算法的目标就是确保所有节点最终达成一致,形成一个统一的决策,从而维持系统的整体一致性。
共识算法经过近半个世纪的发展,种类繁多,流程复杂。不同的共识算法的复制模式和一致性级别往往不同。一般来说,我们可以根据共识算法的复制模式和一致性保证对其进行分类。
根据共识算法的复制模式,可以将共识算法分为三个大类别:
单主复制:客户端将所有写入操作发送到单个节点(主库),该节点将数据更改事件流发送到其他副本(从库)。读取可以在任何副本上执行,但从库的读取结果可能是陈旧的。
多主复制:客户端将每个写入发送到几个主库节点之一,其中任何一个主库都可以接受写入。主库将数据更改事件流发送给彼此以及任何从库节点。
无主复制:客户端将每个写入发送到几个节点,并从多个节点并行读取,以检测和纠正具有陈旧数据的节点。
而根据共识算法的一致性保证,可以大致将共识算法分为两个大类别:
强一致性共识算法:能够对外提供线性一致性或顺序一致性的保证。线性一致性是最强的一致性级别,顺序一致性比线性一致性稍弱。如果共识算法提供强一致性,那么当一个数据写入操作完成后,任何后续的读操作都能读取到最新的数据值。强一致性保证了数据的一致性,但通常会付出一定的性能代价,因为它通常需要确保每一个写入操作在大多数相关节点上都被确认并完成。
弱一致性共识算法:通常只提供最终一致性的保证,包括因果一致性、单调读一致性、读己之所写一致性、会话一致性等。如果共识算法提供弱一致性,那么系统将允许在某些条件下节点之间的数据状态不同步,直到某个时间点或某个条件满足后,系统才会最终达到一致性。弱一致性在设计上更加灵活,可以提高系统的性能和可用性,但在一定程度上牺牲了一致性。
对于大多数共识算法,我们都可以根据上文的介绍识别它对应的复制模式和一致性保证。对于不同的共识算法,它们通常在性能、可用性和一致性保证等方面各不相同。通常情况下,提供更高级别的一致性级别会导致性能和可用性的下降。
(3)主流共识算法简介
通过前文的介绍,相信读者已经对共识算法的概念、作用和分类有了一定了解,接下来让我们简单了解两个著名共识算法,并感受不同共识算法在可用性、一致性、性能和存储成本等方面的区别。
1. Raft:Raft 是一种基于领导者的强一致性共识算法。Raft 算法将共识过程分解为几个关键的子过程:领导者选举、日志复制和安全性保证等。通过将共识过程结构化,Raft 算法不仅易于理解,而且便于实现,工业界已经有多种 codebase 的 Raft 算法成熟实现。
-
Raft 协议的易理解性使得开发者能够快速实现和调试分布式系统,同时其强一致性的保证提高了系统的可靠性和可维护性。
作为强一致性算法, Raft 对于每次写入都要求大多数节点(半数以上)响应,并且需要持久化日志。因此性能相比弱一致性算法有一定差距,并且在大多数节点宕机时无法提供服务,可用性较差。此外,Raft 额外的持久化日志也带来了更高的存储成本。
2. Quorum:Quorum 是最出名的无主共识算法。在 Quorum 中,分布式系统的每一份数据拷贝对象都被赋予一票,只有每一个读操作获得的票数大于最小读票数,每个写操作获得的票数大于最小写票数时才能读或者写。在节点间数据不一致时,其通过读修复和反熵来保证数据间的最终一致性。Quorum 通常只能提供最终一致的弱一致性保证。
-
由于 Quorum 一致性级别较低,无法满足很多应用场景对一致性的需求,Cassandra 目前已经使用了 Paxos 算法来管理其轻量事务。
Quorum 的读写最小票数可以用来作为系统在读、写性能方面的一个可调节参数。写票数 V(w) 越大,则读票数 V(r) 越小,这时候系统读的开销就小。反之则写的开销就小。由于 Quorum 读写都只需要对部分副本操作,因此可以根据读写需求平衡读写最小票数来获得更好的性能。
(4)共识算法统一框架
共识算法是分布式系统中数据读写关键链路的核心组成部分。如果所有共识算法都各自独立地实现其接口,一方面,这将不可避免地导致其他模块需要对各种共识算法进行大量的特殊处理和适配,从而对系统的迭代效率和维护成本产生负面影响。另一方面,这也将给未来系统接入新共识算法的扩展性带来挑战。
因此,为了提高系统架构演进的可扩展性和可维护性,某些分布式系统会考虑设计一种支持不同共识算法实现并对外提供统一接口的共识算法统一框架。
接下来,让我们简单了解 OSDI 2020 Best Paper 提出的共识算法统一框架,感受共识算法统一框架带来的好处。后文也会详细介绍 IoTDB 的共识算法统一框架~
Facebook 的 Delos 通过共享日志抽象出了虚拟共识服务,其不仅能够对应用层透明且允许在虚拟共识服务底层实现不同的共识算法,并且还能够支持不停机更换共识算法。这些优质特性使得其统一了 Facebook 内部的所有元数据管理方案。
如上图所示,Delos 框架将多种复杂的共识算法进行了封装,它通过统一的 VirtualLog API 向上提供接口,并向下集成了多种共识算法。每个 VirtualLog 的一段被称为一个 Loglets,每个 Loglets 可以使用不同的共识算法实现。
Delos 实现了动态维护 Loglets 的功能,从而支持动态更换共识算法。值得一提的是,Delos 项目一开始实现了一个非常简单的基于 Zookeeper 的共识方案 ZKLoglets。仅仅 8 个月后,它就成功地动态更换了所有线上环境的共识算法,从 ZKLoglets 到自研的 NativeLoglets,实现了十倍的性能提升。
02
IoTDB 共识算法框架
上文介绍了共识算法和共识算法统一框架的相关概念,接下来,让我们深入 IoTDB 内部的共识算法框架,了解 IoTDB 所支持的共识算法。
(1)概念与示例
Node 对应一个进程,可以是一个物理节点,也可以是一个容器运行时环境。
ConsensusGroup 定义了一个共识组,对应分片技术的一个分片,其逻辑上存在于副本数个 Node 上,共同维护一份数据的多个副本。
Peer 对应 ConsensusGroup 在一个 Node 上的逻辑单元,一个 ConsensusGroup 在一个 Node 上对应一个 Peer,其在副本数个 Node 上共拥有副本数个 Peer。
IStateMachine 是共识组管理底层存储引擎数据的结构,用于维护数据的一份本地副本,每个 IStateMachine 逻辑上被一个 Peer 所拥有,存在于一个 Node 上。
IConsensus 是共识层对外提供服务的入口,管理了该 Node 中的所有 Peer,提供增删共识组、共识组成员变更、读写共识组数据等功能。开发者可以在创建 IConsensus 时通过配置参数来指定不同一致性级别的共识算法实现(例如弱一致性共识算法 IoTConsensus 和强一致性共识算法 RatisConsensus)。
如下图所示,在 IoTDB 的共识算法统一框架中,用户的一份数据将会被一个共识组 ConsensusGroup 管理,这个共识组由多个 Peer 组成,每个 Peer 通过用户自定义的 IStateMachine 管理该数据的一份本地副本。IConsensus 管理本进程中所有属于不同的共识组 Peers。IConsensus 是共识层对外提供服务的入口,包括创建共识组、共识组成员变更、读写共识组数据等功能。
对于一个 4 节点 3 副本的集群,假设集群将所有数据均分分成了 2 个分片,则示意图如下:
在上图中可以明确如下信息:
整个集群共存在 4 个 Node,每个 Node 上存在一个 IConsensus 实例。
整个集群共有 2 个分片,即有 2 个 ConsensusGroup。ConsensusGroup1 存在于 Node1,Node2 和 Node3 上,ConsensusGroup2 存在于 Node2,Node3 和 Node4 上。
Node1 的 IConsensus 管理了 ConsensusGroup1 的 Peer1,对应了 1 个 IStateMachine 实例,拥有集群的一半数据。
Node2 的 IConsensus 管理了 ConsensusGroup1 的 Peer2 和 ConsnesusGroup2 的 Peer1,对应了 2 个 IStateMachine 实例,拥有集群的全量数据。
Node3 的 IConsensus 管理了 ConsensusGroup1 的 Peer3 和 ConsnesusGroup2 的 Peer2,对应了 2 个 IStateMachine 实例,拥有集群的全量数据。
Node4 的 IConsensus 管理了 ConsensusGroup2 的 Peer3,对应了 1 个 IStateMachine 实例,拥有集群的一半数据。
(2)当前支持共识算法
目前共识层支持三种不同的共识算法,它们分别是:
SimpleConsensus:单副本强一致性共识算法。专为单副本场景优化。
RatisConsensus:多副本单主强一致性共识算法。基于 Java 语言的 Raft 算法工业实现 Apache Ratis。
IoTConsensus:多副本多主弱一致性共识算法。这是一种为 IoT 场景设计的自研共识算法,使用 2 副本即可实现高可用。通过异步复制和大量工程优化,实现了近乎实时的同步性能。在物联网场景下,不同设备的写入操作通常是独立进行的,因此并发写入请求之间基本不存在写写冲突。基于这一特性,IoTConsensus 能够提供最终一致性甚至会话一致性的语义。
(3)如何配置 IoTDB 共识算法
对于 IoTDB,我们可以通过修改 iotdb-system.properties 中下面的配置项对共识算法进行配置。
共识算法选择
config_node_consensus_protocol_class:ConfigNode 使用的共识算法,可选择 SimpleConsensus 或者 RatisConsensus。
schema_region_consensus_protocol_class:元数据使用的共识算法,可选择 SimpleConsensus 或者 RatisConsensus。
data_region_consensus_protocol_class:普通数据使用的共识算法,可选择 SimpleConsensus 或者RatisConsensus 或者 IoTConsensus。
副本数
副本数标识了数据分片冗余存储的个数。通常来说,副本数越多,系统可用性越强,但会占用更多的存算资源。
schema_replication_factor:元数据副本数
data_replication_factor:普通数据副本数
负载均衡
Leader balance 是指让 leader 均匀分布在各个节点上,从而对于写流量进行负载均衡。我们可以选择对于 RatisConsensus 和 IoTConsensus 是否打开 leader balance。
enable_auto_leader_balance_for_ratis_consensus:对 RatisConsensus 开启 leader balance。
enable_auto_leader_balance_for_iot_consensus:对 IoTConsensus 开启 leader balance。
(4)如何选择 IoTDB 共识算法
1. SimpleConsensus:SimpleConsensus 是一种轻量级的单副本共识算法。在关注硬件成本的用户场景中,为了节约资源和成本,一些用户倾向于使用单副本的方式部署集群。SimpleConsensus 的设计旨在为用户提供一种经济的选择,以适应对扩展性要求较高而对可用性要求较低的场景。通常被用在单副本部署的场景中。
2. RatisConsensus:RatisConsensus 是 Raft 协议的工业实现,为一款支持 Multi-Raft 的强一致性共识算法。通常被用在 ConfigNode 和元数据的管理场景中。值得一提的是,IoTDB 社区联合 Ratis 社区对 RatisConsensus 进行了大量工程优化,使得 RatisConsensus 在提供强一致性保证的同时,拥有相对理想的性能。
3. IoTConsensus:IoTConsensus 是由 IoTDB 团队自研的弱一致性多主共识算法。该算法结合了 IoT 场景的业务特性,进行了大量针对性的架构和工程优化,因此不仅性能更优,而且具有更强的可用性。与 RatisConsensus 需要 3 副本才能实现高可用相比,IoTConsensus 仅需 2 副本即可实现高可用,常配置两副本,这也使得其存储成本更低。该算法通常用于时序数据的管理场景。
03
总结
分布式系统通过横向拓展,将数据冗余存储在多个物理节点,实现了高可用和高性能。在这个过程中,共识算法用于协调多个节点的行为,从而保证集群整体的一致性。
与业界其他时序数据库相比,Apache IoTDB 是第一个也是唯一一个提出并应用共识协议统一框架的系统。用户可以根据性能、可用性、一致性和存储成本等需求,灵活选择不同的共识算法。
值得一提的是,对于 IoTDB 社区专为 IoT 场景自研的多主弱一致性共识算法 IoTConsensus,尽管其已经拥有较高的性能和可用性,但由于使用 WAL 进行副本同步,仍然存在性能和可用性的优化空间。近期,IoTDB 团队通过使用自研数据文件 TsFile 进行数据同步,从而与 WAL 解耦,进一步优化了 IoTConsensus 的性能和可用性,从而提出了新的 FastIoTConsensus 共识算法。该新共识算法正在内测中,即将上线,敬请期待~
本文是 Apache IoTDB 分布式三部曲中的最后一篇。通过 Apache IoTDB 分布式三部曲对集群、分片与负载均衡、副本与共识算法的介绍,相信您已经深入了解了 IoTDB 的整体分布式架构与设计思想,期望三部曲系列能帮助您合理运用 IoTDB 的共识算法、负载均衡等分布式策略来应对复杂的物联网数据管理挑战!