乐趣区

关于数据库:MatrixCube揭秘102300行实现的完整分布式存储系统MatrixKV

上篇文章具体的介绍了 MatrixCube 的性能与架构,MatrixCube 是 MatrixOne 数据库取得分布式能力的重要组件。明天咱们将通过一个简略的分布式存储 demo 试验来残缺的体验下 MatrixCube 的性能。

MatrixKV 我的项目介绍

这个 demo 我的项目叫做 MatrixKV,在 Github 的仓库地址是:https://github.com/matrixorig…。

MatrixKV 是一个简略的分布式强统一 KV 存储系统,采纳 Pebble 作为底层的存储引擎,MatrixCube 作为分布式组件,以及自定义了最简略的读写申请接口。用户能够非常简单的在像任意一个节点发动读写数据的申请,也能够从任意一个节点读到须要的数据。

如果对 TiDB 架构比拟相熟的同学能够把 MatrixKV 等同于一个 TiKV+PD,而 MatrixKV 其中应用的 RocksDB 换成了 Pebble。

本次试验以 Docker 模仿一个小型 MatrixKV 集群的模式,来进一步阐明 MatrixCube 的性能与运作机制。

第一步:环境筹备

工具筹备

咱们这个试验须要用到 docker 与 docker-compose 工具,因而须要装置好 docker 与 docker-compose。一般来说能够间接装置 Docker-desktop,外面自带了 docker 引擎,CLI 工具及 Compose 插件。官网提供了各种操作系统的残缺安装包:https://www.docker.com/produc…

装置好之后能够通过以下命令查看是否装置实现,如果顺利完成装置的话会显示相应版本。

docker -v
docker-compose -v

Docker 自身是跨平台的,因而本次试验对操作系统没有要求,不过举荐 macOS12+ 或者 CentOS8+(因为残缺验证过)。本次教程是在 macOS12 的环境中容许的。本次试验因为只有单机的一块硬盘,Prophet 对各个节点进行负载再均衡 Rebalance 的性能无奈应用,因而在本次试验中会呈现节点负载和数据量并不平衡的状况,而在残缺的多机系统中能够更好体验这个性能。

Clone 代码

将 MatrixKV 代码 Clone 到本地。

git clone https://github.com/matrixorigin/matrixkv  

第二步:MatrixKV 集群配置

在上一篇文章中,咱们提到过 MatrixCube 基于 Raft 构建分布式共识协定,因而须要至多三个节点来作为最小部署规模,而最后的三个节点都属于调度用的 Prophet 节点。咱们这次试验筹备的这个小型集群有四个节点,其中三个为 Prophet 节点,一个为数据节点。咱们以 docker 进行容器包装的模式来在单机上进行模仿。

Prophet 节点设置

咱们能够看到在 /cfg 文件夹中有 node0-node3 的配置文件,其中 Node0-Node2 均为 Prophet 节点,Node3 为数据节点。Prophet 节点的配置以 Node0 举例如下如下:

#raft-group 的 RPC 通信地址,节点之间通过这个地址来发送 raft message 和 snapshot。addr-raft = "node0:8081" 

#对客户端凋谢的地址,客户通过这个端口和 cube 来交互自定义的业务申请。addr-client = "node0:8082"

#Cube 的数据寄存目录,每个节点会依据这个目录所在的磁盘统计存储的应用状况,上报给调度节点。dir-data = "/tmp/matrixkv"

[raft]
#Cube 会对 Raft 的写申请做 batch,会把多个写申请合并到一个 Raft-Log,只做一次 Raft 的 Proposal,这个配置指定一个 Proposal 的大小,这个 #配置取决于利用的具体情况
max-entry-bytes = "200MB"

[replication]
# 1. 一个 raft-group 的正本最大的 down time,当一个正本的 down time 超过这个值,调度节点就会认为这个正本用久的故障了,# 而后会在集群中抉择一个适合的节点来从新创立这个正本。如果前面这个节点又重新启动了,那么调度节点会告诉这个正本
# 销毁本人。# 2. 这里的默认设置个别是 30 分钟,这个工夫咱们认为是设施个别呈现故障能够在 30 分钟内实现故障解决复原,如果超过这个工夫阐明曾经无奈  # 复原。在这里咱们为了做试验的不便,设置成 15 秒。max-peer-down-time = "15s"

[prophet]
#该 Prophet 调度节点的名称
name = "node0"
#该 Prophet 调度节点对外的 RPC 地址
rpc-addr = "node0:8083"
#指定该节点为 Prophet 节点
prophet-node = true

[prophet.schedule]
# Cube 集群中的所有节点都会定期发送心跳到调度的 Leader 节点,当一个节点超过肯定的工夫都没有发送心跳,# 那么调度节点会把这个节点的状态批改为 Down,并且会把这个节点上,所有的 Shard 在集群其余节点来重建,# 当这个节点复原后,这个节点上的所有 Shard 都会收到销毁的调度音讯。# 这里也是为了试验不便设置成 10 秒,默认也是 30 分钟。max-container-down-time = "10s"

#Prophet 中内嵌一个 ETCD 作为存储元数据的组件
[prophet.embed-etcd]
#Cube 的 Prophet 调度节点会先后启动, 假如咱们有 node0, node1, node2 三个调度节点, 第一个启动的是 node0 节点, 那么 node0 节点就会
#组成一个只有一个正本的 etcd, 对于 node0 而言, `join` 参数不须要填写, 前面的 node1, node1 启动的时候, `join` 设置为 node1
#的 Etcd 的 Peer address
join = ""
#内嵌 Etcd 的 client address
client-urls = "http://0.0.0.0:8084"
#内嵌 Etcd 的 advertise client address, 不填写, 默认和 `client-urls` 统一
advertise-client-urls = "http://node0:8084"
#内嵌 Etcd 的 peer address
peer-urls = "http://0.0.0.0:8085"
#内嵌 Etcd 的 advertise peer address, 不填写, 默认和 `peer-urls` 统一
advertise-peer-urls = "http://node0:8085"

[prophet.replication]
#每个 Shard 最多多少个正本,当 Cube 的调度节点周期性巡检的时候,发现 Shard 的正本个数和这个值不匹配的时候,会执行创立正本或者删除正本的调 #度操作。max-replicas = 3 

Node1 与 Node2 的配置除了须要在 ETCD 配置局部中 join 后面的节点,其余的简直与 Node0 没有差异。

数据节点设置

而 Node3 作为数据节点,则配置绝对比较简单,除了 prophet-node 设置成 false 以外,其余没有须要额定配置的局部。

addr-raft = "node3:8081"
addr-client = "node3:8082"
dir-data = "/tmp/matrixkv"

[raft]
max-entry-bytes = "200MB"

[prophet]
name = "node3"
rpc-addr = "node3:8083"
prophet-node = false
external-etcd = [
    "http://node0:8084",
    "http://node1:8084",
    "http://node2:8084",
] 
Docker-Compose 设置

Docker-compose 将依据 docker-compose.yml 中的配置来进行容器启动,其中咱们须要将每个节点的数据目录改成本人指定的目录。咱们以 Node0 为例。

node0:
    image: matrixkv
    ports:
      - "8080:8080"
    volumes:
      - ./cfg/node0.toml:/etc/cube.toml
      # /data/node0 须要批改成用户指定的某个本地目录
      - /data/node0:/data/matrixkv
    command: 
      - --addr=node0:8080
      - --cfg=/etc/cube.toml
      # shard will split after 1024 bytes
      - --shard-capacity=1024

第三步:集群启动

配置好这些选项后,在 MatrixKV 代码库中,咱们曾经写好了构建镜像的 dockerfile 及启动构建流程的 Makefile。

咱们间接在 MatrixKV 的门路下运行 make docker 命令,它会将 MatrixKV 整体打包成镜像。

# 如果是 MAC X86 架构平台或者 Linux,能够间接运行以下命令(make docker)
#如果是 MAC 的 ARM 版本,则须要将 Makefile 中的 docker build -t matrixkv -f Dockerfile . 改成 docker buildx build --platform linux/amd64 -t matrixkv -f Dockerfile .

make docker

另外留神国内用户如果可能碰到 go 源站速度极慢无奈下载依赖库的状况,能够在 Dockerfile 中减少 go 的中国源站设置:

RUN go env -w GOPROXY=https://goproxy.cn,direct

而后通过 docker-compose up 命令将 MatrixKV 的镜像别离依据不同的节点配置启动四份,从而造成咱们的 Node0 到 Node3 的四节点集群。

docker-compose up

在 docker desktop 中咱们应该就能够看到咱们的 4 个 MatrixKV 的节点都以镜像的模式启动了。

在看到如下日志中呈现各个节点启动监听 8080 端口的时候,就代表集群曾经启动实现。

同时能够看到在咱们指定的数据目录中曾经开始生成了很多存储数据的文件夹以及一些初始文件。

敞开集群的话能够在启动的命令行中进行过程即可,或者也能够在 Docker desktop 中以图形化界面形式进行任意节点。

第四步:读写申请接口与路由

在启动好集群之后,咱们就能够对集群进行读写数据的申请。MatrixKV 包装了几个非常简单的数据读写接口:

  • 数据写入 SET:

    curl -X POST  -H 'Content-Type: application/json' -d '{"key":"k1","value":"v1"}' http://127.0.0.1:8080/set    
    
  • 数据读取 GET:

    curl http://127.0.0.1:8080/get?key=k1
    
  • 数据删除 DELETE

    curl -X POST -H 'Content-Type: application/json' -d '{"key":"k1"}' http://127.0.0.1:8080/delete
    

上一篇文章中介绍了 MatrixCube 中的 Shard Proxy,这个组件能够使得咱们能够从集群的任意一个节点发动申请,不论是写入,读取还是删除的申请,Shard Proxy 都会主动将申请路由到相应的解决节点上。

比方咱们能够在 node0 上写入数据,而在 node0 到 node3 上都能够进行读取,是齐全一样的。

// 向 node0 发动写入申请
curl -X POST  -H 'Content-Type: application/json' -d '{"key":"k1","value":"v1"}' http://127.0.0.1:8080/set
// 从 node0-node3 进行读取
curl http://127.0.0.1:8080/get?key=k1
curl http://127.0.0.1:8081/get?key=k1
curl http://127.0.0.1:8082/get?key=k1
curl http://127.0.0.1:8083/get?key=k1

这里如果试验的系统配置及写入读取数据规模更大一些的话,大家也能够验证一些更极其的场景,比方有多个客户端在疾速的读取各个节点的数据,而每次写入的数据在客户端读到的时候都能够保障是最新的以及统一的,通过这种形式能够验证 MatrixCube 的强一致性,保障任何时刻从任何节点读到的数据都是最新的以及统一的。

第五步:数据分片查问与决裂

MatrixCube 会在写入的数据量达到肯定级别的时候产生 Shard 决裂,在 MatrixKV 中,咱们将 Shard 的大小设置成了 1024Byte。因而写入数据超过这个尺寸的数据会产生决裂。MatrixKV 提供了一个简略的查问以后集群或者以后节点中有多少个 Shard 的接口。

# 以后集群中的 Shard 状况
curl http://127.0.0.1:8080/shards
#以后节点中的 Shard 状况
curl http://127.0.0.1:8080/shards?local=true

咱们启动集群后能够看到初始状态下集群只有 3 个 Shard,id 别离为 4, 6, 8, 而他们理论存储的节点在 node0,node2 与 node3 中。

而在咱们通过以下命令写入一个超过 1024Byte 的数据之后,咱们能够看到 node0,node2 与 node3 中的 Shard 全副进行了决裂,每个原来的 Shard 都造成了两个新的 Shard,初始状态下的 3 个 Shard 变成了 11,12,13,15,16,17 六个 Shard。

#test.json 是测试数据,数据内容须要严格依照 Key,Value 格局标准,比方{"key":"item0","value":"XXXXXXXXXXX"}
curl -X POST  -H 'Content-Type: application/json' -d@test.json http://127.0.0.1:8083/set

同时咱们依然能够在任意一个节点拜访到咱们写入的数据。

第六步:节点变动与正本生成

接下来咱们再来看下 MatrixCube 的高可用保障的性能。咱们能够通过 Docker desktop 来手动关停单个容器,以此来模仿实在环境中的机器故障状况。

在第五步中咱们输出一个较大数据之后零碎整体存在 6 个 Shard,每个 Shard 有 3 个 Replica。咱们当初将 node3 手动关掉。

尝试再拜访 node3 的命令均以失败告终。

然而从其余节点发动读申请,数据依然都能够读取,这也就是分布式系统对整体高可用性的体现。

依照后面咱们的设置,store3 的心跳 10 秒内没有发到 Prophet,Prophet 会认为这个 Store 曾经下线,而通过查看目前正本状况发现,所有的 Shard 都只有两个 Replica,为了满足 3 正本的要求,Prophet 会开始主动去寻找闲暇节点,将 Shard 复制到下面,在咱们这里也就是 node1,那么咱们再来看下每个节点 Shard 的状况。

能够看到 node1 中以前是没有 Shard 的,当初也与 node0 和 node2 一样都有 6 个 shard。这也就是 Prophet 主动的正本生成性能,始终保证系统中有三份副原本保障高可用性。

除了正本生成以外,如果呈现问题的是某一个 Shard 的 Raft Group Leader,那么这个 Shard 的 Raft Group 会从新发动选举,而后先选举新的 Leader,再由 Leader 发动申请进行新的正本生成。这个大家能够自行试验,并且通过日志的信息进行验证。

MatrixKV 代码扫描

通过整个试验咱们曾经残缺体验了在 MatrixCube 帮忙下将一个单机的 KV 存储引擎 Pebble 变成了一个分布式的 KV 存储。而其中须要 MatrixKV 自身实现的代码是非常简单的。总的来说就只有 4 个 go 文件,不到 300 行代码就能够实现 MatrixKV 的全副搭建。

  • /cmd/matrixkv.go: 整体程序启动的入口,进行最根本的初始化并启动服务。
  • /pkg/config/config.go: 定义了一个 MatrixKV 整体配置的数据结构。
  • /pkg/metadata/metadata.go:定义了用户与 MatrixKV 读写交互申请的数据结构。
  • /pkg/server/server.go:这是 MatrixKV 的最主体性能,其中次要做了三件事:

    • 定义 MatrixKV server 的数据结构。
    • 定义 Set/Get/Delete 等相干申请的 Executor 具体实现。
    • 调用 Pebble 库作为单机存储引擎,实现 MatrixCube 指定的 DataStorage 接口,将 MatrixCube 的 Config 项设置到相应办法上。

福利工夫

请加小助手微信,ID:MatrixOrigin001

  1. 发送您的 MatrixKV 初体验残缺录屏,即可取得限量 MatrixOrigin T 恤一件。
  2. 发送您的 MatrixKV 初体验残缺录屏并公布在 CSDN,即可取得价值 200 元的京东卡 + 限量 MatrixOrigin T 恤一件。

总结

作为 MatrixCube 系列的第二篇,咱们通过基于 MatrixCube 和 Pebble 所实现的一个自定义分布式存储系统 MatrixKV 的试验,更进一步的展现了 MatrixCube 的运作机制,同时也展现了 300 行代码即能够迅速的搭建一个残缺强统一的分布式存储系统。下一期咱们将带来 MatrixCube 更加深刻的代码精讲,敬请期待。

退出移动版