暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PolarDB PostgreSQL版 VFS 模块介绍

内核开发者 2023-09-19
600

关于 PolarDB PostgreSQL 版

PolarDB PostgreSQL 版是一款阿里云自主研发的云原生关系型数据库产品,100% 兼容 PostgreSQL,高度兼容Oracle语法;采用基于 Shared-Storage 的存储计算分离架构,具有极致弹性、毫秒级延迟、HTAP 、Ganos全空间数据处理能力和高可靠、高可用、弹性扩展等企业级数据库特性。同时,PolarDB PostgreSQL 版具有大规模并行计算能力,可以应对 OLTP 与 OLAP 混合负载。

VFS模块原理

Linux内核中,使用VFS模块抽象文件系统一致的行为,并提供统一的上层文件I/O调用接口,屏蔽掉底层不同文件系统具体的实现,因此,其可以灵活的加载/卸载多种不同的文件系统。

PolarDB PostgreSQL版是基于 Shared-Storage 的存储计算分离架构,也需要适配底层不同的 Shared-Storage 实现,为了达到和Linux内核一样灵活的适配不同的共享存储文件系统,PolarDB PostgreSQL提出了PolarDB VFS(PolarDB Virtual File System)模块,其大体架构如下图所示。

PolarDB VFS模块针对上层其他模块传入的I/O操作进行了分类处理,将I/O操作交给对应的文件系统实现接口进行处理。分类的依据是文件路径,一个具体的文件路径只对应一个具体的文件系统,不允许出现一个文件路径对应多个文件系统的现象。

PolarDB VFS模块目前支持三种具体的文件系统实现:PolarDB File System文件系统、本地Direct I/O文件系统、本地Buffer I/O文件系统。在此架构下,PolarDB VFS模块可以灵活地添加/删除底层不同的文件系统实现,并且还可以在VFS模块中对I/O操作执行不同粒度的监控。

VFS模块实现

路径协议

在前述章节“PolarDB PostgreSQL版手动部署与共享存储配置介绍”中,提到了PoalrDB PostgreSQL使用参数polar_datadir配置共享存储的路径,该参数的格式为:polar_datadir=[protocol://]path。

PolarDB PostgreSQL借鉴了URL的格式,对polar_datadir路径进行了类似的划分,分为protocol和path两部分,其中protocol就是对底层不同文件系统实现的标识,支持三类:“pfsd://”、“file-dio://”、“file://”,分别对应PolarDB File System文件系统、本地Direct I/O文件系统、本地Buffer I/O文件系统。对于每个具体路径协议的内部实现原理,将会在接下来的文章中介绍。

一个文件的I/O操作,最开始传入的参数都是路径,因为路径中包含protocol字段,因此,可以确定其对应的具体文件系统实现,即使后期如read、write等I/O操作不再需要文件路径,但由于PolarDB VFS模块中已经缓存了句柄fd对应的具体文件系统实现类别,因此,仍然可以将read、write等I/O操作交给正确的文件系统实现接口去处理。

FD缓存

根据上述内容可以得知,部分I/O操作函数是不带有路径的,因为只根据路径协议是无法辨别对应的文件系统实现类别。另外,由于不同的文件系统实现可能会同时被PolarDB PostgreSQL其他模块调用,而且不同的文件系统实现接口对于不同路径的文件,其返回的句柄fd有可能是相同的。

为了避免上述两个问题带来的影响,PolarDB VFS模块需要提供一层FD缓存来屏蔽底层不同文件系统的具体实现,其大体架构如下图所示:

FD缓存向上提供统一的VFS fd,其内部维护VFS fd与PFSD fd、DIO fd、BIO fd之间的映射关系,其维护映射关系使用的数据结构是一个空闲VFS fd链表,将空闲的VFS fd放到链表中维护,分配时从空闲链表头中取出第一个即可。链表的每一个VFS fd元素的结构及介绍如下:

typedef struct vfs_vfd
{
	int				fd; /* 对应的PFSD fd、DIO fd或者BIO fd的具体值 */
	int				kind; /* 对应的文件系统实现类别:PFSD、DIO或者BIO */
	int 			type; /* 文件所属的类型,PolarDB PostgreSQL将文件按照其用途划分了几个类型,比如:数据、事务、WAL日志等,主要用于监控 */
	int				next_free; /* 下一个空闲VFS fd元素位置 */
	char			*file_name; /* 文件路径 */
} vfs_vfd;

I/O操作函数

为了方便将文件I/O操作函数进行管理,以及兼容不同文件系统实现类别的I/O操作函数,PolarDB VFS模块需要一个文件I/O操作函数的集合,于是定义了vfs_mgr结构体,如下所示,将open、read、write等I/O操作函数汇聚到该结构体中。

typedef struct vfs_mgr
{
	int (*vfs_env_init)(void);
	int (*vfs_env_destroy)(void);
	int (*vfs_mount)(void);
	int (*vfs_remount)(void);
	int (*vfs_open)(const char *path, int flags, mode_t mode);
	int (*vfs_creat)(const char *path, mode_t mode);
	int (*vfs_close)(int fd);
	ssize_t (*vfs_read)(int fd, void *buf, size_t len);
	ssize_t (*vfs_write)(int fd, const void *buf, size_t len);
	ssize_t (*vfs_pread)(int fd, void *buf, size_t len, off_t offset);
	ssize_t (*vfs_pwrite)(int fd, const void *buf, size_t len, off_t offset);
	int (*vfs_stat)(const char *path, struct stat *buf);
	int (*vfs_fstat)(int fd, struct stat *buf);
	int (*vfs_lstat)(const char *path, struct stat *buf);
	off_t (*vfs_lseek)(int fd, off_t offset, int whence);
	off_t (*vfs_lseek_cache)(int fd, off_t offset, int whence);
	int (*vfs_access)(const char *path, int mode);
	int (*vfs_fsync)(int fd);
	int (*vfs_unlink)(const char *path);
	int (*vfs_rename)(const char *oldpath, const char *newpath);
	int (*vfs_fallocate)(int fd, off_t offset, off_t len);
	int (*vfs_ftruncate)(int fd, off_t len);
	DIR *(*vfs_opendir)(const char *path);
	struct dirent *(*vfs_readdir)(DIR *dir);
	int (*vfs_closedir)(DIR *dir);
	int (*vfs_mkdir)(const char *path, mode_t mode);
	int (*vfs_rmdir)(const char *path);
	const struct vfs_mgr* (*vfs_mgr_func)(const char *path);
} vfs_mgr;

PolarDB PostgreSQL内核创建了全局数组vfs_mgr polar_vfs[]和全局变量polar_vfs_switch,其中polar_vfs[0]默认挂载Linux内核提供的open、read、write等文件I/O操作函数,polar_vfs[1]中的文件I/O操作函数默认为NULL,可以挂载为PolarDB VFS层实现的vfs_open、vfs_read、vfs_write等文件I/O操作函数。基于此,PolarDB PostgreSQL内核提供了polar_open、polar_read、polar_write等文件I/O操作函数,polar_*相关函数利用polar_vfs和polar_vfs_switch实现了转发操作,均是直接调用的polar_vfs[polar_vfs_switch].*相关函数实现的。

由于PolarDB VFS层实现在插件polar_vfs中,因此如下图所示,共享存储模式下,当polar_vfs插件加载后,可以将polar_vfs_switch切换为1,这样PolarDB PostgreSQL内核其他模版调用polar_*相关函数时,便被转发到了PolarDB VFS模块实现的vfs_*文件I/O操作函数中。

同样的原理,PolarDB VFS层通过全局数组vfs_mgr vfs[]将不同文件系统实现的I/O操作函数进行汇聚,通过路径协议来将一个文件的I/O操作转发到对应的文件系统实现的接口中,具体如下图所示。


VFS模块使用

PolarDB VFS层模块主要涉及到以下几个组件:polar_vfs插件、polar_datadir路径配置参数、polar_enable_shared_storage_mode共享存储配置参数。

  • polar_vfs插件默认放到polar_internal_shared_preload_libraries参数中,被PolarDB PostgreSQL内核启动时默认加载;

  • polar_datadir路径配置了共享存储路径下位置,其中包含的路径协议表明了该路径下文件的I/O操作函数最终转发到的具体文件系统实现类别,关于共享存储路径下存储的内容,将会在接下来的文章中介绍;

  • polar_enable_shared_storage_mode配置了该节点是否开启共享存储模式,只有打开了该参数,才可以使能PolarDB VFS模块正常工作。

具体的部署方式,可以参见前述文章“PolarDB PostgreSQL版手动部署与共享存储配置介绍”。

关于 PolarDB PostgreSQL 版

PolarDB PostgreSQL 版是一款阿里云自主研发的云原生关系型数据库产品,100% 兼容 PostgreSQL,高度兼容Oracle语法;采用基于 Shared-Storage 的存储计算分离架构,具有极致弹性、毫秒级延迟、HTAP 、Ganos全空间数据处理能力和高可靠、高可用、弹性扩展等企业级数据库特性。同时,PolarDB PostgreSQL 版具有大规模并行计算能力,可以应对 OLTP 与 OLAP 混合负载。

VFS模块原理

Linux内核中,使用VFS模块抽象文件系统一致的行为,并提供统一的上层文件I/O调用接口,屏蔽掉底层不同文件系统具体的实现,因此,其可以灵活的加载/卸载多种不同的文件系统。

PolarDB PostgreSQL版是基于 Shared-Storage 的存储计算分离架构,也需要适配底层不同的 Shared-Storage 实现,为了达到和Linux内核一样灵活的适配不同的共享存储文件系统,PolarDB PostgreSQL提出了PolarDB VFS(PolarDB Virtual File System)模块,其大体架构如下图所示。

PolarDB VFS模块针对上层其他模块传入的I/O操作进行了分类处理,将I/O操作交给对应的文件系统实现接口进行处理。分类的依据是文件路径,一个具体的文件路径只对应一个具体的文件系统,不允许出现一个文件路径对应多个文件系统的现象。

PolarDB VFS模块目前支持三种具体的文件系统实现:PolarDB File System文件系统、本地Direct I/O文件系统、本地Buffer I/O文件系统。在此架构下,PolarDB VFS模块可以灵活地添加/删除底层不同的文件系统实现,并且还可以在VFS模块中对I/O操作执行不同粒度的监控。

VFS模块实现

路径协议

在前述章节“PolarDB PostgreSQL版手动部署与共享存储配置介绍”中,提到了PoalrDB PostgreSQL使用参数polar_datadir配置共享存储的路径,该参数的格式为:polar_datadir=[protocol://]path。

PolarDB PostgreSQL借鉴了URL的格式,对polar_datadir路径进行了类似的划分,分为protocol和path两部分,其中protocol就是对底层不同文件系统实现的标识,支持三类:“pfsd://”、“file-dio://”、“file://”,分别对应PolarDB File System文件系统、本地Direct I/O文件系统、本地Buffer I/O文件系统。对于每个具体路径协议的内部实现原理,将会在接下来的文章中介绍。

一个文件的I/O操作,最开始传入的参数都是路径,因为路径中包含protocol字段,因此,可以确定其对应的具体文件系统实现,即使后期如read、write等I/O操作不再需要文件路径,但由于PolarDB VFS模块中已经缓存了句柄fd对应的具体文件系统实现类别,因此,仍然可以将read、write等I/O操作交给正确的文件系统实现接口去处理。

FD缓存

根据上述内容可以得知,部分I/O操作函数是不带有路径的,因为只根据路径协议是无法辨别对应的文件系统实现类别。另外,由于不同的文件系统实现可能会同时被PolarDB PostgreSQL其他模块调用,而且不同的文件系统实现接口对于不同路径的文件,其返回的句柄fd有可能是相同的。

为了避免上述两个问题带来的影响,PolarDB VFS模块需要提供一层FD缓存来屏蔽底层不同文件系统的具体实现,其大体架构如下图所示:

FD缓存向上提供统一的VFS fd,其内部维护VFS fd与PFSD fd、DIO fd、BIO fd之间的映射关系,其维护映射关系使用的数据结构是一个空闲VFS fd链表,将空闲的VFS fd放到链表中维护,分配时从空闲链表头中取出第一个即可。链表的每一个VFS fd元素的结构及介绍如下:

typedef struct vfs_vfd
{
	int				fd; /* 对应的PFSD fd、DIO fd或者BIO fd的具体值 */
	int				kind; /* 对应的文件系统实现类别:PFSD、DIO或者BIO */
	int 			type; /* 文件所属的类型,PolarDB PostgreSQL将文件按照其用途划分了几个类型,比如:数据、事务、WAL日志等,主要用于监控 */
	int				next_free; /* 下一个空闲VFS fd元素位置 */
	char			*file_name; /* 文件路径 */
} vfs_vfd;

I/O操作函数

为了方便将文件I/O操作函数进行管理,以及兼容不同文件系统实现类别的I/O操作函数,PolarDB VFS模块需要一个文件I/O操作函数的集合,于是定义了vfs_mgr结构体,如下所示,将open、read、write等I/O操作函数汇聚到该结构体中。

typedef struct vfs_mgr
{
	int (*vfs_env_init)(void);
	int (*vfs_env_destroy)(void);
	int (*vfs_mount)(void);
	int (*vfs_remount)(void);
	int (*vfs_open)(const char *path, int flags, mode_t mode);
	int (*vfs_creat)(const char *path, mode_t mode);
	int (*vfs_close)(int fd);
	ssize_t (*vfs_read)(int fd, void *buf, size_t len);
	ssize_t (*vfs_write)(int fd, const void *buf, size_t len);
	ssize_t (*vfs_pread)(int fd, void *buf, size_t len, off_t offset);
	ssize_t (*vfs_pwrite)(int fd, const void *buf, size_t len, off_t offset);
	int (*vfs_stat)(const char *path, struct stat *buf);
	int (*vfs_fstat)(int fd, struct stat *buf);
	int (*vfs_lstat)(const char *path, struct stat *buf);
	off_t (*vfs_lseek)(int fd, off_t offset, int whence);
	off_t (*vfs_lseek_cache)(int fd, off_t offset, int whence);
	int (*vfs_access)(const char *path, int mode);
	int (*vfs_fsync)(int fd);
	int (*vfs_unlink)(const char *path);
	int (*vfs_rename)(const char *oldpath, const char *newpath);
	int (*vfs_fallocate)(int fd, off_t offset, off_t len);
	int (*vfs_ftruncate)(int fd, off_t len);
	DIR *(*vfs_opendir)(const char *path);
	struct dirent *(*vfs_readdir)(DIR *dir);
	int (*vfs_closedir)(DIR *dir);
	int (*vfs_mkdir)(const char *path, mode_t mode);
	int (*vfs_rmdir)(const char *path);
	const struct vfs_mgr* (*vfs_mgr_func)(const char *path);
} vfs_mgr;

PolarDB PostgreSQL内核创建了全局数组vfs_mgr polar_vfs[]和全局变量polar_vfs_switch,其中polar_vfs[0]默认挂载Linux内核提供的open、read、write等文件I/O操作函数,polar_vfs[1]中的文件I/O操作函数默认为NULL,可以挂载为PolarDB VFS层实现的vfs_open、vfs_read、vfs_write等文件I/O操作函数。基于此,PolarDB PostgreSQL内核提供了polar_open、polar_read、polar_write等文件I/O操作函数,polar_*相关函数利用polar_vfs和polar_vfs_switch实现了转发操作,均是直接调用的polar_vfs[polar_vfs_switch].*相关函数实现的。

由于PolarDB VFS层实现在插件polar_vfs中,因此如下图所示,共享存储模式下,当polar_vfs插件加载后,可以将polar_vfs_switch切换为1,这样PolarDB PostgreSQL内核其他模版调用polar_*相关函数时,便被转发到了PolarDB VFS模块实现的vfs_*文件I/O操作函数中。

同样的原理,PolarDB VFS层通过全局数组vfs_mgr vfs[]将不同文件系统实现的I/O操作函数进行汇聚,通过路径协议来将一个文件的I/O操作转发到对应的文件系统实现的接口中,具体如下图所示。

VFS模块使用

PolarDB VFS层模块主要涉及到以下几个组件:polar_vfs插件、polar_datadir路径配置参数、polar_enable_shared_storage_mode共享存储配置参数。

  • polar_vfs插件默认放到polar_internal_shared_preload_libraries参数中,被PolarDB PostgreSQL内核启动时默认加载;
  • polar_datadir路径配置了共享存储路径下位置,其中包含的路径协议表明了该路径下文件的I/O操作函数最终转发到的具体文件系统实现类别,关于共享存储路径下存储的内容,将会在接下来的文章中介绍;
  • polar_enable_shared_storage_mode配置了该节点是否开启共享存储模式,只有打开了该参数,才可以使能PolarDB VFS模块正常工作。

具体的部署方式,可以参见前述文章“PolarDB PostgreSQL版手动部署与共享存储配置介绍”。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论