开发者学堂课程【PolarDB for PostgreSQL 开源人才初级认证培训课程:共享存储原理与实践】学习笔记,与课程紧密连接,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/1077/detail/15557
共享存储原理与实践
内容介绍:
一、整体介绍
二、PolarFS 实践
三、存储形态
一、整体介绍
1、总体架构
(1)传统 RDS 分布式架构
首先来看一下传统的 RDS 架构。RDS 架构在高可用的实现上采用了主备复制的模式,通过 Binlog 逻辑来保证高可用。
存在的问题:
首先增加计算节点需要同步扩容存储,这样就导致存储的成本随着节点数的上升而上升。
其次扩展只读节点比较麻烦,因为需要进行数据重建。当在数据量比较大的情况下,如果进行数据重建会耗时比较久,特别是在故障场景下可能会影响具体的耗时。
(2)PolarDB 分布式架构
接下来看一下 PolarDB 是如何实现这个问题的。不同于传统的主备复制,PolarDB 采取的共享存储架构-存储和计算分离。中间通过 PolarFS 的文件系统来和存储进行共享数据。
RW 首先调用文件系统的 API,文件系统经过内部处理把数据写入到共享存储中。数据的可靠性以共享存储的多副本机制来保证,因此在 DB 层面就不用关心多部分数据的写入。对于 DB 而言就是写入了一份数据。在共享存储中天然就存在多副本的机制。当 RW 写入数据之后,RO 感知到数据的变动得益于共享存储,所有的节点就可以共享同一份数据。
PolarFS 需要解决的就是如何感知到 RW 节点的变化。这就是 PolarFS 这个分布式文件系统的主要作用,它天然的解决了一些多读场景下的数据一致性问题。这样设计的好处就是存储和计算节点相分离,存储节点的扩容和计算节点的扩容互相不偶合,可以单独进行扩容并且单独进行一份数据。新增 RO 节点的时候不需要再进行数据重建,可以快速拉起一个 RO 节点,甚至是秒级,并且存储层面也可以单独扩容。当存储不够时,不需要扩容计算节点。
二、PolarFS 实践
1、PolarFS 文件系统
(1)功能
PolarFS 文件系统是专门为 DB 设计的用户态文件系统,它不同于常见的提示文件系统,那些文件系统是实现在类和态的。PolarFS 是实现在用户态并且基于共享存储系统,支持多读的实现,向上提供兼容 posix 的接口。
首先来看一下写节点的数据写入,比如它调用 PolarFS 创建一个目录,PolarFS 把数据写入到一个共享存储中。这时候如果 RO 需要访问这个目录,由于采用了共享存储的机制,数据已经在存储中了,但是从 PolarFS 层面本身就有一些文件系统的语言数据,这个需要在 RO 阶段进行感知。对此通过一个日志同步的方式来让 RO 感知。
(2)特性
高性能:用户态文件系统,减少数据拷贝
动态弹性:存储按需分配,支持在线扩容,按需挂载
高可用:基于共享存储实现,相比主备复制,无需数据拷贝,RTO 分钟级别,RPO=0。
支持一写多读,最高可扩展15个 RO 节点
这里先给大家了解一个整体的一个架构印象。这里设计的一个好处,就是 PolarFS 提供 posix 兼容,然后 DB 可以不用再去大刀阔斧的改造自己的代码,可以以较小的代价实现一个分布式的架构。并且也天然的因为在共享存储的加入下,实现了一个高可用。比如在新增RO 或者 HA 的场景下,相比较主倍复制,并不需要进行数据拷贝,RTO 可以达到分钟级别甚至秒级。
2、PolarFS 元数据
除了文件系统,首先需要了解的就是文件系统的元数据。PolarFS 拥有常见文件系统中的两种概念:目录和文件
元数据的概念:
PolarFS 和常见的文件系统类似,主要有以下几种元数据的概念。
(1)Direntry:记录文件和目录的名字
比如对于 home 目录,这个目录的文件和名字就记录在 Direntry 中。
(2)Inode
描述文件或目录的基本属性,比如某个东西是文件还是目录。对于 home 目录来说,Inode 就是 dir 类型;对于 gcc 文件来说,Inode 就是文件类型。
(3)Biktag
主要是描述一个数据块,这个数据块所属的文件是在文件的哪个位置,并且 block 是对应磁盘上的一个位置。比如再次看 gcc 这个文件,当创建一个 gcc 的文件并进行写入的时候,它就要被分配一个数据块空间,会分配一个 Blocktag。这个 Blocktag 会映射到一个适度的空间,需要分配大于适度的空间的时候就会把多个 block 连接起来,就达到了锁定链接的目的。
系统结构:
在整个文件系统的目录输入结构上,是采用了一个典型的树状结构。
3、PolarFS 磁盘布局
当拿到一个文件系统的时候,首先也是要进行一个 mkFS 的操作。mkFS 的主要作用就是把文件系统的空间进行划分,把底层的共享存储管理起来。假设有一个40GB的存储设备,按照10GB的 chunk 把空间进行一个划分,这样可以分出四个 chunk 的空间。在针对每个 chunk 的一步,又划分了4MB的 block,每一个 block 都是4MB 的大小。一个特殊需要指数的点就是每个 chunk 的第0个 PolarFS 的 superblock,这是用来记录文件系统的元数据信息。最多的就是三类元数据,一个是 blocktag,一个是 Direntry,还有一个就是 Incode。每一个4MB 的 blocktag 可以存储到最多2048个 Direntry 和 Incode。这就意味着每个chunk的下面可以创建2048个文件和目录。除了第0个 block 之外的所有的 datablock,就是存放文件的数据,这些 block 总共有2560个。Blktag 元数据会管理到 datablock,映射到 datablock。这就是 PolarFS 磁盘上的整体布局。
在使用 PolarFS 的文件系统的时候,首先进行格式化的过程就是会把这些元数据在 superblock 中格式化。
4、日志
日志在文件系统主要是保证多个修改的原子性,文件系统对文件的修改往往涉及多个不同类型的元数据修改。比如要同时修改 Incode 、block、dentry 单纯这些元数据的时侯,如果这些修改不能原子性的完成,可能会造成数据的不一致或者损坏,这样可能会导致文件系统的损害,影响文件系统的挂载。因此在常见的文件系统时,引入 journal 的设计,在元数据修改时,先写入到 journal,保证文件系统的数据完整性和一致性。
PolarFS 的日志除了保证事务提交的完整性以外,还有一些用途是通过一写多读来同步数据。PolarFS 的写节点在写入数据之后,RO 节点是需要感知到数据的变动。因为文件系统的内部有许多元数据是需要管理的,当 RW 节点对于内部的元数据做出修改以后,如果要保证一致性,那 RO节点上也需要对这些元数据进行同样的修改,才能保证一致性,这也是通过 journal 日志来进行同步的。
现在来看一看 journal 是怎么进行一写多读的数据同步的。首先journal 是一个文件,它是一个固定大小的空间,这个 head lsn 就是当前事物提交到的一个最新的位点。
比如 RW 按顺序提交了一批事物,当前提交的事物 ID 为10,head lsn 就更新到了10,这是代表当前日志的一个最新的位点。checkpoint的话是指因为日志是一个循环覆盖写的一个过程,如果日志提交了,空间不够了,则需要做一个 checkpoint 操作,把写入,把日志里面的东西催动到磁盘上,就是把日志同步更新到磁盘上。它的变更就已经反应到磁盘的共享存储中。这样就可以把位点不停地推进,把这块空间释放出来用于提交新的 log。这就是一个循环写的过程,即事务提交的一个基本流程。
在 RW 提交了完整的元数据之后,如果 RO要访问这个文件,比如对一个文件进行读写的时候,首先回去读写一下最新的位点,当前日志的位点已经推进了多少。如果已经推进到了10,就定位 ID 为10,表示这个事务已经提交到了10。RO 内部本身会维护一个已有的事物的一个位点。如果把5之前的事务全部同步到了 RO 的节点,这时 RO 就会发现5和10之间还差距了5个,就会把这5个数据同步到 RO 节点中,毕竟内存有回放。如果 RW 改变了 Incode 的一些属性,RO 也能通过这个步骤感知到扩容多少度,也顺便统计到多少个 block。同步完之后把自己的位点也更新到了10,这样就达到了 RW 和 RO 的一致性。这样就实现了一写多读,数据是可以实时感知的。
5、PolarFS 的编译与安装
接下来会重点介绍如何使用 PolarFS 来进行一个共享存储系统的 搭建。
编译:
(1)源码地址
https://github.com/ApsaraDB/PolarDB- FileSystem
首先来访问一下 github 地址,这个是开源的 github 地址,可以在其中下载源代码,随后进行编译安装。代码已经下载到本地了,这里重点实操一下怎样进行编译安装以及 pfs 工具的使用。
代码如下:
chaohai.wch@chaohai PolarDB-FileSystem [master]$ll
total 100
-rwxr-xr-x 1 chaohai.wch users 875 Jul 13 16:21 autobuild.sh
drwxr-xr-x 2 chaohai.wch users 4096 Jul 13 16:22 bin
-rw--r--r--1 chaohai.wch users 1217 Jul 13
16:21 CMakeLists-config.txt
-rw -r--r-- chaohai.wch users 3495 Jul 13 16:21 CMakeLists.txt
drwxr-xr-x2 chaohai.wch users 4096 Jul 13 16:21 conf
drwxr-xr-x2 chaohaiwch users 4096 Jul 1316:21 deploy_scripts
drwxr-xr-x4 chaohaiwch users 4096 Jul 13 16:21 docker
drwxr-xr-x3 chaohai.wch users 4096 Jul 1316:21 docs
drwxr-xr-x2 chaohai.wch users 4096 Jul 13 16:21 etc
drwxr-xr-x2chaohaiwch users 4096 Jul 13 16:21 include
-rwxr-xr-x1 chaohai.wch users 4060 Jul 13 16:21 1install.sh
drwxr-xr-x2chaohai.wch users 4096 Jul 13 16:22 lib
-rw-r--r--1 chaohai.wch users 11369 Jul 1316:21 LICENSE.txt
-rw-r--r--1 chaohai.wch users 1041 Jul 13 16:21 NOTICE.txt
-rw-r--r-- 1 chaohai.wch users 3862 Jul 13 16:21 Readme-CN.md
-rw-r--r--1 chaohai.wch users 4773 Jul 1316:21 Readme-FUSE-CN.md
-rw-r--r-- 1 chaohai.wch users 5225 Jul 13 16:21 Readme-FUSE.md
-rw-r--r-- 1 chaohaiwch users 5027 Jul 1316:21 Readme.md
drwxr-xr-x9 chaohai.wch users 4096 Jul 13 16:21 src
-rwxr-xr-x1 chaohaiwch users 1676 Jul 13 16:21 uninstall.sh
chaohai.wch@chaohai PolarDB-FileSystemImaster]$
(2)文档地址
https://github.com/ApsaraDB/PolarDB-FileSystem/blob/master/Readme-CN.md
这里的文档详细的介绍了 PolarFS 安装所需要的一些依赖包,包括如何编译,如何启动以及如何进行安装部署。这里的代码已经下载下来了看,就直接进行编译。这里的编译比较顺利是因为相关的依赖包已经安装过了。并且需要安装一些 z-log还有 fuse。安装完毕之后就把它进行安装,这里有一个 install 脚本,安装的时候需要进行一个修复的权限,因为需要把一些变更安装到一些目录中。安装完之后系统中就会有 pfs 对应的工具。在这里可以直接执行 pfs 命令,它就会出现很多提示告知 pfs 指示的哪些命令。
使用文件系统的第一步是对文件系统进行格式化,格式化也是使用 pfs 的工具来实行。同样也是需要使用修正的权限,因为需要进行磁盘的格式化。因为 PolarFS 其实支持的是不同的存储形态的,除了块设备之外,还支持一个 PolarStore。PolarStore 是内部自主研发的分布式的一个重要存储,然后调用 mkfs 命令。这个命令下面有很多选项,比如要指令 log 的尺寸大小。num-user 是用来控制一些并发实例的一些编号。如果对于这些选项不是非常清楚的话采用默认值就可以了。
因为如果文件系统之前有被格式化过后,就需要加上 -s 的选项,就是强制格式化。然后只指定一下设备,这个设备就是常见的块设备。对于单节点来说,直接用本地的值就可以了,就是 pfs 共享存储。除了用共享存储的话,也可以支持单机的硬盘,但是单机的硬盘就不能实现跨节点的数据访问了。因为日常开发的话可以用单机来模拟,然后就对这个进行格式化。在此使用61000这个硬盘,然后出现 mkfs 就表示格式化成功了。
接下来看一看格式化成功之后文件系统下面有哪些文件。对此还是需要用到 pfs 这个工具。pfs 工具下面支持的很多命令,比如就用iOS可以看看。对此还是需要修复权限,因为涉及到硬盘的操作,都需要切换到root。可以看到下面有个 pfs journal,这个 journal就是刚才说过通过这个 journal 来进行事务完整性的保证和一写多读数据的同步。这就是整个工具的一个简单的用法。
其中还有很多命令可以去看一看,比如说通过命令可以创建目录,创建文件。在此就来创建一个目录。创建目录使用的是 mkdir 命令。如果没有报错就代表创建成功了,然后还是用 ls 来看一看创建的结果。可以看到这下面已经创建了一个测试的目录,这就是对于整个文件系统用工具来进行的一些基本操作,更多的命令可以自己去尝试下载一个元代码来编译安装。更多的命令的话,可以看看这里的文档如何使用,作用是干什么的等等,这些都写的还是比较清楚的。详细的命令操作指南可以参考文档:
https://github.com/ApsaraDB/PclarDB-FileSystem/blob/master/docs/PFS Tools-CN.md
6、PoalrFS 的部署形态
这里用 PolarDB PG 的架构来举例,首先 PolarDB 分为多个进程,
它使用多个进程来进行数据通信的。首先这是一个主进程,每次要进行新的请求的时候,它会复合一个 work 的子进程,每个进程下面都会引入 PolarFS 的一个客户端,这个客户端就是包含了一些read ,write,open这些常见的操作。然后会调用客户端的接口,和后台的 pfsd 的进程进行通信,这个 pfsdaemon 是 pfsd 的一个后台进程。在安装部署的时候,就已经安装到了那个 logo 下面。然后这两个中间的话是用的共享内存的一个通信,就是用共享内存来作为一个通信的信道。
共享内存的好处:
共享内存有什么好处?最大的好处就是可以减少数据的拷贝,比如客户端要写一个 buffer,直接把数据加到共享内存中。然后 prd 文件系统就直接可以把这个数据取出来,就是间接实现的一个零拷贝的操作,然后再把它写入到硬盘里面。这就是整个部署的一个架构。
刚才已经调用了 install,它安装在了这个目录下面可以看到,这就是刚才安装的 pfsdaemon。最简单的启动方式是可以直接调用 start pfsd。随后进入到安装目录中,唯一需要填写的参数是如果不需要进行额外的修改的话,只有默认人数参数。这里还没有安装动态库,尝试着把动态库安装就可以使用了。
代码如下:
Pfs version:
libpfs_version_("pfsd-build-desc-2df7ef0-Thu Jul 14 15:29:58 CST 2022")
chaohai.wch@chaohai PolarDB-FileSystem [master] $ sudo pfs-C disk ls/nvme6n1/
File 1 4194304 Thu Jul 14 15:36:03 2022 pfs-paxos
File 1 1073741824 Thu Jul 14 15:36:04 2022 ·pfs-journal
total 2105344(unit:512Bytes)
chaohai.wch@chaohai PolarDB-FileSystem[master] $ sudo pfs-C disk mkdir/nvme6n1/testDir chaohai.wch@chaohai PolarDB-FileSystem[master] $ sudo pfs-C disk ls/nvme6n1/
File 1 4194304 Thu Jul 14 15:36:032022 .pfs-paxos
File 1 1073741824 Thu Jul 14 15:36:04 2022 ·pfs-journal
Dir 1 Thu Jul 14 15:38:35 2022 testDir
total 2105344(unit:512Bytes)
chaohai.wchachaohaiPolarDB-FileSystem[master]$ll /usr/local/polarstore/pfsd/bin/ total 2396
-rwxr-xr-x 1 root root 1059 Jul 14 15:30 clean_pfsd.sh
-wxr-xr-x1 root root 3780 Jul 14 15:30 mount_pfs_fuse.sh
-rwxr-xr-x1 root root 1851032 Jul 1415:30 pfsdaemon
-rwxr-xr-x1 root root 122496 Jul 14 15:30 pfsd_shm_tool
-rwxr-xr-x1 root root 456656 Jul 14 15:30 pfs-fuse
-rwxr-xr-x1 root root 1653 Jul 14 15:30 start pfsd.sh
-rwxr-xr-x1 root root 1930 Jul 14 15:30 stop_pfsd.sh
-rwxr-xr-x1 rootroot 2516 Jul 14 15:30 umount_pfs_fuse.sh
chaohai.wch@chaohai PolarDB-FileSystem(master]$
7、Fuse 访问 PolarFS
接下来重点看一下如何通过 Fuse 访问 PolarFS。Fuse 就是类似于一个文件系统访问的中间层,它对上接标准的文件系统的语音操作,对下对接不同的文件系统。通过一些标准的命令可以直接访问到底层的 PolarFS。比如要看 PolarFS 的目录的话,需要用 PFS 工具才能进行机械创建文件和删除文件。但是接入 fuse 之后,可以直接使用 fuse 的命令,例如 mkdir 这些值去创建目录然后放到 pfs 中。fuse 的安装可以使用 github 中的文档。
接下来看一下如何使用 fuse。
(1)挂载 fuse
首先使用 fuse 的话就要先把 fuse 挂载,把 fuse挂载到 pfs 上。这里已经提供了一个现成的脚本进行操作,这个脚本在刚才安装的时候已经部署到机器上了。
三个参数:
这个脚本下面需要三个参数。
Disknme:代表块设备名称,就是刚才进行格式化的块设备。可以通过命令 lsblk 列出所有可用块设备信息。
rw/ro:代表所启动实例为启动读写实例或者只读实例。因为这需要创建目录和创建文件等写操作,所以这里启动一个读写实例。
mount_dir指fuse挂载目录,就是可以在这个目录下面进行操作。
说明:挂载FUSE on PFS首先会启动 pfsdaemon 后台进程,然后启动pfs-fuse 进程并挂载PFS到制定目录。
当出现 fuse mount success 时,就代表着 fuse 已经挂载成功了。同时这个步骤会启动 pfsdaemon 这个程序,fuse 的操作就会经过 pfsdaemon 的进程的处理。挂载成功之后就可以进入 fuse 进行目录访问了。进入到刚才的 fuse 目录,方才执行 ll 操作因为就直接显示了一个 pfs testDir,就是刚才用 pfs 工具创建的目录,因为这个目录已经通过 fuse 挂载到 pfs 上面。创建 mkdir 成功之后再创建一个文件,文件也创建成功了。随后打开文件进行简单的写入,对文件进行修改,然后看一下文件的内容。
(2)通过 fuse 访问 PFS
如果要确认 fuse 挂载成功之后是通过 pfs 进行读写的,就可以使用 pfs 的工具来看一下,下面的 pfs 文件系统中是否真正存在这些东西。这里可以看到刚才创办的目录和文件都已经同步到了 pfs 的文件系统,这就说明通过 fuse 已经成功地访问到了 pfs 文件系统。
从这一点可以看出 fuse 最大的作用就是可以简化对文件系统的一些操作,可以使用一些原生的命令,就可以达到访问 pfs 文件系统的目的。
(3)解除 fuse 挂载
Fuse 使用完如果不需要的话也可以执行以下两种命令对他执行 umount 操作。
1. /usr/local/polarstore/pfsd/bin/umoqntpfs fuse.sh [mount dir/all]
2. 停止 pfsdaemon 后台进程
sudo/usr/local/polarstore/pfsd/bin/stoppfsd.sh[diskname]
三、存储形态
1、PoalrStore
接下来看一下 PolarFS 支持的一些存储形态。PolarFS 支持多种不同的挂载形态,支持不同的存储介质。主要支持的第一类就是 PolareStore,这是阿里云数据库内部自研的一个分布式的共享文件系统,它是通过 RDMA 在存储层进行数据多副本机制的通讯以及复制。现在阿里云官网上购买的 PolarDB 产品是基于 PolarDB 的多形态实现的。
支持的版本:
PolarDB-MySQL:5.6/5.7/8.0版本
PolarDB-PostgreSQL11
PolarDB-Oracle兼容
这些版本目前是基于 PolarDB 的产品形态上。
2、ESSD
SSD 是一个标准的分布式的块设备共享存储,它也是在阿里云上可以直接购买的。这个的部署方式和之前单机的比较类似。区别就是它在底层是一个分布式的共享存储,所有的操作步骤和在单机上的演示是一样的。通过 ESSD 可以达到一些多读,进行数据共享以及数据同步一致性的效果。
3、Ceph
接下来再看一下开源的实现,Ceph 是一个业界比较流行的分布式的存储系统,它对上层暴露了多种的存储接口,典型的有一个 RBD。RBD就是一个块存储的一个接口。RedosGW 就是一个对象存储的接口,下面可以对接 S3等等。Ceph FS 相当于 Ceph 要暴露一个文件系统的接口,主要用到的是 RDB 形态,就是通过 RBD 模拟一个块设备。这里主要是模拟了一个单机节点,搭建一个 Ceph 的集群来实现,主要就是在一台机器上指定了三个存储盘。Ceph 文件已经提前搭建好了,看到 ls block 的时候就可以看到 7n1,8n1,9n1已经映射到了一个 Ceph 的集群中,由 OSD进程来负责管理。
使用 Ceph最重要的是创建一个存储池,这样就可以执行相应的命令。creat rbd cool 就是创建了一个 rbd 的存储池,然后就是把存储值创建一个卷。例如创建开了一个 20级存储的卷,然后通过 rdb 命令把这里的卷大小映射为一个块设备就是通过 map。最后在这里也可以看到一个 RDB 名称,就是刚才通过 Ceph 映射出来的块设备。同时也可以对这个块设备进行操作,这个块设备的操作和单机硬盘的操作是一样的,首先还是对它进行 mk-FS 操作,因为是块设备所以还是加入了 -c disk。因为模拟的是一个20GB 大小的块设备,所以这里就格式化了两个 chunk,可以看到文件系统已经格式化成功了。再执行一下一个创建步骤的操作,可以看到已经成功通过 Ceph 的块设备进行的 PolarFS 文件的读写了。如果有兴趣可以尝试用不同的开源快介质搭建 PolarFS。