MRS IoTDB的集群架构
基于Multi-Raft的分布式对等架构
MRS IoTDB集群是完全对等的分布式架构,既基于Raft协议避免了单点故障问题,又通过Multi-Raft协议避免了单一Raft共识组带来的单点性能问题,同时对分布式协议的底层通讯、并发控制和高可用机制做了进一步优化。
首先,整个集群的所有节点构成一个元数据组(MetaGroup),只用于维护存储组的元数据信息。例如下图蓝灰色框所示的一个4节点的IoTDB集群,全部4个节点构成一个元数据组(MetaGroup);
其次,根据数据副本数构造数据组。例如副本数为3,则构造一个包括3个节点的数据组(DataGroup)。存储组用于存储时间序列数据及对应的元数据。
分布式系统中通常以多副本的方式实现数据的可靠存储。同一份数据的多个副本存储在不同的节点中且必须保证一致,因此需要使用Raft共识协议来保证数据的一致性,它将一致性的问题拆分成了几个相对独立的子问题,即领导者选举、日志复制、一致性保证等。Raft协议中有以下重要的概念:
(1)Raft组。Raft组中有一个通过选举产生的leader节点,其他节点是follower。当一个写入请求到来时,首先要提交给leader节点处理,leader节点先在自己的日志里面记录下这个写入请求,然后将这条日志分发到follower节点。
(2)Raft日志。Raft通过日志的方式保证操作不会丢失,日志中维护了一个 Commit编号和Apply编号。如果一条日志被Commit,就代表目前集群中超过半数的节点都收到并持久化了这条日志。如果一条日志被Apply,就表示当前节点执行了这条日志。当某些节点出现故障并重新恢复时,该节点的日志就会落后于领导者的日志。则在这个节点追上领导人的日志之前,它不能向外界正常提供服务。
元数据分层管理
元数据管理策略是MRS IoTDB分布式设计中的要点。在进行元数据管理策略设计时首先要考虑元数据在读写流程中的用途:
写入数据时需要元数据进行数据类型、权限等合法性检查。查询数据时需要元数据进行查询路由。同时,由于时序数据场景中元数据庞大,还需要考虑元数据对内存资源的消耗。
现有的元数据管理策略要么采用将元数据交由元数据节点专门管理的方式,这种方法会降低读写性能; 要么采用在集群所有节点全量保存元数据的方式,这种方式会消耗大量的内存资源。
为了解决上述问题,MRS IoTDB设计了双层粒度元数据管理策略,其核心思想是通过将元数据拆分为存储组和时间序列两层分别管理:
(1) 存储组元数据:元数据组(MetaGroup)包含了查询数据时的路由信息,存储组(Storage Group)的元数据信息在集群所有节点上全量保存。存储组的粒度较大,一个集群内部的存储组数量级远远小于时间序列的数量级。因此在集群所有节点上对这些存储组元数据的保存,大大减少了内存的占用。
元数据组中的每个节点称为元数据持有者,采用Raft协议来保证每个持有者与同组的其他持有者的数据一致性。
(2) 时间序列元数据:数据组(DataGroup)中的时间序列元数据中包含了数据写入时需要的数据类型、权限等信息,这些信息保存在数据组所在节点(集群部分节点)上。由于时间序列元数据的粒度较小,数量远远多于存储组元数据,因此这些时间序列元数据保存在数据组所在的节点上,避免了不必要的内存占用,同时也能通过存储组元数据的一级过滤快速定位,同时数据组的Raft一致性也避免了时间序列元数据存储的单点故障。
数据组中的每个节点称为数据分区持有者,采用Raft协议来保证每个持有者与同组的其他持有者的数据一致性。
该方法将元数据按存储组和时间序列两层粒度分别在元数据持有者和数据分区持有者中管理,由于时间序列数据和元数据在数据组内同步,因此每次数据写入不需要进行元数据的检查与同步操作,仅需要在修改时间序列元数据时进行存储组元数据的检查与同步操作,从而提高系统性能。例如创建一个时间序列并进行50万次数据写入的操作中,元数据检查与同步操作从50万次降至1次。
元数据分布
根据元数据分层管理可知,元数据分为存储组元数据和时间序列元数据。
存储组元数据在全集群所有的节点上都有副本,属于MetaGroup组。
时间序列元数据只在对应的DataGroup上存储,存储一些时间序列的属性,字段类型,字段描述等信息。时间序列元数据的分布方式和数据分布方式一样,都是通过slot hash产生。
时间序列数据分布
分布式系统实现中基于哈希环和环上查找算法将时序数据按照存储组进行分区。将集群各个节点按哈希值放到哈希环上,对于到来的一个时间序列数据点,计算这个时间序列名称所对应的存储组的哈希值并放置到哈希环上,在环上按顺时针方向进行搜索,找到的第1个节点就是要插入的节点。
使用哈希环进行数据分区时,容易出现两个节点的哈希值的差较小的情况,因此在使用一致性哈希环的基础上引入虚拟节点,具体做法是将每个物理节点虚拟成若干个,并将这些虚拟节点按照哈希值放置到哈希环上,在很大程度上避免了数据倾斜的情况,使数据分布得更加均匀。
首先,整个集群预设10000个slot,均匀将此10000个slot分布在各个DataGroup上。如下图所示,IoTDB集群有4个DataGroup,整个集群有10000个slot,则平均每个DataGroup有10000/4=2500个slot.由于DataGroup的数量等于集群节点数4,也就相当于平均每个节点2500个slot.
其次, 完成slot到DataGroup、Time Partition和time series的映射。
IoTDB集群根据raft协议划分成多个DataGroup组,每一个DataGroup组中包含多个slot,而每一个slot中包含多个time partition,同时每一个time partition中包含多个time series,构成关系如下图所示:
最后,通过Hash计算slot的值,完成输入存储组和时间戳到slot的映射: