共计 6116 个字符,预计需要花费 16 分钟才能阅读完成。
作为新一代的数据库系统,MatrixOne 也是以当今风行的分布式架构为根底来设计的。除了存储引擎与计算引擎之外,分布式组件也曾经成为古代数据库设计的必选项。数据库系统在分布式组件的撑持下展现出越来越弱小的可扩展性和高可用性,然而同时也必须面对分布式环境中的一致性,可靠性等挑战。
MatrixOne 中的 MatrixCube 正是一个这样的分布式组件,它能够将任意单机存储引擎扩大成分布式的存储引擎,存储引擎只须要关怀单机的存储设计,而不须要去思考分布式环境中的各种问题。MatrixCube 是一个相当宏大的组件,MatrixOne 社区将输入一系列文章与教程来进行全面揭秘。
本文作为第一篇文章,将首先从性能与概念上解释 MatrixCube 能做什么,以及它的架构是什么样的。
MatrixCube 是什么
MatrixCube 是一个 Golang 实现的基于 Multi-Raft 的自带调度能力的分布式强一致性存储框架。
MatrixCube 的设计指标是让开发人员可能轻松地实现各种强统一的分布式存储服务。咱们能够应用 MatrixCube 来构建一些常见的分布式存储服务,比方:分布式 Redis、分布式 KV 等等。而 MatrixOne 也是一个通过 MatrixCube 构建的分布式数据库,在没有 MatrixCube 的状况下,MatrixOne 就是个单机的数据库,MatrixCube 使得咱们能够搭建一个小型的集群。然而 MatrixCube 并不与 MatrixOne 紧耦合,MatrixCube 能够对接任意的其余存储引擎,使得其取得同样的分布式存储能力,比方咱们能够让 MatrixCube 对接一个 RocksDB,Pebble,或者 Redis。
图 -MatrixCube 的作用
MatrixCube 有以下几个性能个性:分布式,高可用,强统一,主动平衡,用户自定义。
分布式
这个很好了解,单机零碎就是一台机器的零碎,分布式系统就是很多机器组成的零碎,所有分布式问题都是在解决协调多台机器共同完成一件事件的问题。与单机只须要操作一套硬件相比,分布式环境中的多台机器须要有大量的协调与沟通机制,同时须要对其中可能出问题的中央进行解决。比方让计算机解决两个数字的运算,在一台机器上间接一套代码就能运行了,然而在分布式环境中咱们须要设计一套机制如何将这个运算拆成不同的子工作交给不同的机器,在每台机器实现本人的局部之后再将各自的后果通过某种机制合并到一起造成最终的后果。而这其中如果某台机器故障,或者因为网络通信的问题导致机器无奈连贯,这些异常情况都须要分布式组件进行解决,保障整个集群依然能实现工作。MatrixCube 就是为了实现多台机器的分布式数据存储而实现的一套分布式框架。
图 - 分布式系统
高可用
作为一个数据库系统,存数据是其最起码的职责。在一个分布式环境中,每台机器都会有肯定的出问题概率,不论是硬件环境还是软件环境都有 fail 的可能性,为了能继续提供服务保证系统的可用性,咱们往往会采纳将同一份数据在复制多个正本的形式,将每个正本放在不同的机器上,以此来晋升可用性,同时因为多正本的存在,在用户来拜访数据的时候咱们也能够通过多台机器同时提供服务来晋升零碎的吞吐能力。应用 MatrixCube 实现的存储服务反对高可用,因为 Raft 协定的选举机制,如果一个集群领有 2*N+1
的正本数量,那么集群在 N
个正本故障的时候,还可能失常的提供读写服务。
强统一
因为多正本的存在,用户能够读取任意节点上的正本。而强统一就是为了保障这些正本之间的数据始终都是统一的,如果在某个正本数据有更新的时候也会先将其余正本同步更新之后才会响应用户来读取,这样用户能始终读到最新的数据。另外一个绝对的概念就是最终一致性,区别在于写入之后马上就通知用户能够读了,然而用户如果马上去读其余正本的时候可能还没有复制实现,所以依然会读到老数据。Matrixcube 提供强统一的读写接口,并且承诺一旦数据写入胜利了,后续的读操作就不会读到一个古老的值,肯定会读到之前写入的数据或者更新的数据。MatrixCube 采纳了 Multi-Raft 的形式。Raft 是目前应用最宽泛的分布式一致性协定,解释 Raft 协定和运作机制的文章行业内十分丰盛,这里就不具体开展。简略的来说 Raft 协定通过 Leader 选举以及日志复制的办法,保障集群在呈现如机器宕机或者故障的状况下能够始终保持对外提供数据的一致性,且始终维持最新的数据。
图 - 强统一(Follower1)与最终统一(Follower2)
主动平衡
作为分布式存储系统,除了通过强统一的正本复制机制保障高可用性以外,咱们还应该尽可能多地将多台机器的资源利用起来,以达到更高的应用效率。一个合格的分布式存储应该让每个节点之间的存储压力大致相同,同时对每个节点的拜访压力大致相同,不至于在某些节点上面临过大的存储或者拜访压力,这样整个零碎的性能就会因而受到影响。而 MatrixCube 就具备这样的调度与主动平衡能力,能够在多节点间放弃存储与负载的平衡,并且在节点发生变化时进行存储与拜访负载的调度,以达到从新均衡。在 MatrixCube 中,咱们提供了三种级别的主动平衡:
- 实现各节点存储空间的平衡,以高效利用各节点存储资源;
- 各节点的 Raft-Group Leader 的平衡,因为读写申请都须要从 Leader 通过,以此来达到读写申请的负载平衡;
- 各节点 Table 数据分布的平衡,因为某些表可能是热门数据会被频繁拜访,以此来实现表级别的读写申请平衡。
用户自定义
MatrixCube 提供相当灵便的用户自定义能力。MatrixCube 不限度单机的数据存储引擎,并且定义了 DataStorage 接口,任何实现 DataStorage 的存储引擎都能够接入 MatrixCube。而且 MatrixCube 反对自定义的读写申请,使用者只须要实现这些申请在单机上的逻辑,分布式相干的细节全副交给 MatrixCube。因而用户能够十分不便地接入各种不同的单机存储引擎,以及自定义各种不同的读写申请命令。
MatrixCube 的架构与运作机制
MatrixCube 基本概念
咱们须要先理解一些概念来帮忙咱们更好地了解 MatrixCube。
- Store:MatrixCube 是一个分布式存储的框架,所以咱们的数据会寄存在很多的节点上,咱们把集群中的一个节点称为一个 Store。
- Shard:数据在 MatrixCube 集群中是分片存储的,每个数据分片咱们称之为一个 Shard。一个 Store 中能够治理多个 Shard。
- Replica:为了保障存储服务的高可用,每个 Shard 的数据是存储多份的,并且散布在不同的 Store 上,Shard 的一个数据正本咱们称之为一个 Replica。所以一个 Shard 会蕴含多个 Replica,每个 Replica 中的数据都是一样的。
- Raft-Group:通过多正本咱们保障了数据的高可用,为了保证数据的
一致性
,咱们采纳Raft
协定来做数据共识,一个 Shard 的多个 Replica 会组成一个 Raft-Group。
MatrixCube 性能组件
- DataStorage: 应用 MatrixCube 就必须要定义一个 DataStorage,用来存储单机数据。咱们须要针对存储服务的特点来设计对应的 DataStorage。MatrixCube 默认提供了一个残缺的基于 KV 的 DataStorage,因为基于 KV 的存储能够满足大部分的场景。
- Prophet: Prophet 是一个调度模块,主要职责是负责 Auto-Rebalance 以及维持每个 Shard 的 Replica 个数。每个 Store 以及 Shard 的 Leader Replica 都会定期上报心跳信息给 Prophet,Prophet 会依据这些信息来做出调度决定。MatrixCube 须要在集群中指定哪些节点承当调度的职责。
- Raftstore: Raftstore 是 MatrixCube 最外围的组件, 其中实现了 Store,Shard,Raft-Log 相干的元数据存储,对 Multi-Raft 协定的反对,全局路由表的构建以及每个节点上的读写 Shard Proxy 代理性能。
MatrixCube 的整体架构
图 -MatrixCube 整体架构
MatrixCube 的工作机制
系统启动与配置
在零碎初始化的时候,MatrixCube 会依据集群中每个节点的配置文件来进行初始化。在 MatrixCube 中,一共有两种类型的节点,一种是调度节点,也就是 Prophet 节点,另一种是数据节点,只存数据,没有调度性能。两种节点之间目前无奈相互转换,都是在零碎初始化的时候指定好的。MatrixCube 的最后三个节点必须都是 Prophet 节点,这也是 MatrixCube 所能组成的最小规模集群。Prophet 的三个节点形成一个 Raft-Group,其中会选举出一个 leader,所有的调度申请与信息上报都会到这个 Prophet Leader 上。Prophet 的节点数能够进行配置,然而以 Raft 协定为基准,必须是 2 *N+ 1 个节点(N 为不小于 0 的整数)。
数据存储与决裂
在零碎开始启动运行之后,用户开始往零碎中导入数据。MatrixCube 在初始化时已读入一个数据分片 Shard 的大小配置,比方 1GB 一个 Shard。用户写入数据还没有达到 1GB 的时候,会继续写入同一个 Shard,而同时每次写入的数据会同步在三个节点中更新 Shard,这三个 Shard 会形成一个 Raft-Group,其中也会选举出一个 Leader,对这个 Shard 的读写申请与信息上报全副会由这个 Leader 来解决。直到一个 Shard 达到 1GB 时,此时 MatrixCube 会启动 Shard 决裂机制,也就是把一个 Shard 平均拆分成两个 Shard,也就是 1 个 GB 的分片变成了 2 个 500MB 的分片,而这个过程是同步在三个节点中产生的,也就是说同时将生成 6 个 Shard,而他们将组成新的 2 个 Raft-Group。
图 -Shard 决裂
用户读写响应与路由
用户以接口的模式与 MatrixCube 交互,从而来发动对数据的读写申请。因为 Raft-Group 的存在,只有每个 Raft-Group 的 Leader 能力响应读写申请。而用户的申请可能并不一定间接指向 Raft Group Leader,因而咱们就须要一个路由机制,来让用户的申请能被正确的转到它该去的中央。因而每个节点上都会有一个 Shard Proxy,这些 Proxy 会定期接管 Prophet 给它的全局路由表。咱们后面提到 Prophet 会承受各个 Store 与各个 Shard 的 Leader 给的心跳信息,因而 Prophet 是有一张全局的路由信息表的。这个时候 Shard Proxy 就会晓得用户申请读写那份数据应该在哪个 Store 的哪个 Shard 中。这样的话用户无论向集群中哪个节点发动读写申请,最终失去的后果都是一样的,用户不须要关怀 Shard 在外部存在什么中央。
假如咱们有一个 3 个 Store 节点的集群,初始状态如下:
Range | Store1 | Store2 | Store3 | |
---|---|---|---|---|
Shard1 | [key1-key10) | Leader | Follower | Follower |
Shard2 | [key10-key20) | Follower | Leader | Follower |
Shard3 | [key20-key30) | Follower | Follower | Leader |
用户别离向 key1,key10 和 key20 数据发送申请,下图阐明了申请如何通过 Shard Proxy 路由组件并被转发。
图 -Shard Proxy 对用户申请的转发
节点变动与数据搬迁
在整个集群产生节点变动时,比方集群须要扩缩容,或者呈现机器或者网络故障的时候,咱们就要进行集群的调度。通常来讲,咱们的 Prophet 会有一个超时机制,在某个节点超过咱们规定的工夫没有心跳上报的时候,咱们这时就认为这个节点曾经下线,咱们将开始数据搬迁流程。在节点缩小的状况下,Prophet 会检测到一部分 Shard 没有足够的 Replica 组成一个残缺的 Raft-Group,这时就须要在现有的节点中找到存储空间比拟充裕的节点,在其中创立这一部分 Shard 的 Replica。而在节点减少的状况下,Prophet 会发现多出的节点存储空间比拟充裕,这时会将集群中的一部分 Shard 进行重新分配,以达到一个各节点绝对平衡的状态。在节点变动的时候,除了 Prophet 会进行调度意外,每个被毁坏的 Shard 组成的 Raft-Group 也会视状况进行调度,如果少了 Raft-Group 的 follower,那在实现 Shard 的新 Replica 生成后会从新组成残缺的 Raft-Group;如果少了 Raft-Group 的 Leader,那剩下的两份 Replica 就会先选举出新的 Leader,再去新的节点上实现 Replica 与 Raft-Group 的生成。
在下图的例子中咱们看到,初始状态的三节点在减少第四节点的状况下,每个节点的 4 个 Shard 被均摊到了 4 个节点上,而原本不平衡的 Leader 数量也均摊到了每个节点上
图 - 节点减少时的数据搬迁
在这些机制的组合工作下,MatrixCube 就能够将一个单机的存储引擎扩大到一个分布式场景中来应用。
MatrixCube 与 TiKV 的区别
很多社区的小伙伴在之前都接触过 TiDB 分布式数据库的架构,以及其中负责提供分布式能力的组件 TiKV 以及负责调度的模块 Placement Driver。实际上 MatrixCube 的性能根本相当于 TiKV 与 PD 的联合,MatrixCube 除了还在开发中的分布式事务能力以外,其余的高可用,强统一以及调度的能力与 TiKV+PD 根本保持一致。
MatrixCube 与 TiKV+PD 次要有三点区别:
- TiKV+PD 是一个残缺的服务,所有的读写细节都曾经在外部封装实现,用户通过与其定义好的接口与其进行交互。而 MatrixCube 是一个 Library,无奈独自运行,必须与存储引擎一起工作。而且 MatrixCube 将读写的申请命令交给了用户定义,使用者能够自行去实现各自不同存储引擎的读写申请命令,只须要实现 MatrixCube 提供的存储引擎接口即可与 MatrixCube 对接,由 MatrixCube 负责各自分布式的相干细节。
- PD 的调度性能次要体现在存储空间的调度与 Raft Group 的 Leader 的调度,能够在正本级别达到负载平衡。而 MatrixCube 除了在实现这两点外,还实现了表级别的数据分布平衡,从而使得对表的读写申请也能达到绝对平衡的状态。
- TiKV 由 Rust 实现,而 PD 是由 Go 实现。因而这套构造在对接过程中须要肯定的中间层接口。而 MatrixCube 整体都是由 Go 所实现,因而不存在这样的问题。
总的来说 MatrixCube 更加看重开发灵活性,开发者能够非常灵活的利用 MatrixCube 去实现不同地分布式存储引擎。
下期预报
为了向开发者展现 MatrixCube 的应用,MatrixOne 社区筹备了一个非常简单的 KV 存储的例子,用 MatrixCube 对接 Pebble 存储引擎实现了一个分布式存储系统。