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

PolarDB PostgreSQL版Direct I/O实现原理

PolarDB 2025-02-06
64

关于 PolarDB PostgreSQL 版

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

背景

为了支持一写多读架构,PolarDB PostgreSQL版本构建了VFS层作为I/O操作中间层,以向上层代码逻辑屏蔽对于底层存储设备的具体I/O操作,VFS层向上层提供了和传统操作系统一样的I/O操作接口,因此上层应用可以在无需大量修改的情况下通过VFS层来访问不同的底层存储设备。

在前述章节“PolarDB PostgreSQL版 VFS 模块介绍”中,介绍了VFS模块的实现原理与使用方法。PolarDB PostgreSQL版本VFS模块支持三类不同的文件系统:PolarDB File System文件系统、本地Direct I/O文件系统、本地Buffer I/O文件系统,分别对应路径协议:“pfsd://”、“file-dio://”、“file://”。接下来详细介绍下PolarDB PostgreSQL中对于Direct I/O文件系统的实现原理。

Direct I/O原理

为了实现DirectIO模式的I/O操作覆盖所有的共享存储路径下的文件,本文从VFS层中进行设计,为VFS层新增一套I/O接口实现,这套I/O接口专门用于DirectIO模式的I/O操作,通过路径识别来将对应的I/O操作流转给DirectIO模式的I/O接口,实现具体的I/O操作。

Direct I/O模块

在VFS模块中实现DirectIO模式的I/O接口,需要满足上层代码逻辑不改动的情况下,需要满足DirectIO模式的三个硬性条件:缓存地址对齐、文件偏移量对齐、单位读写内容大小对齐。具体从vfs_read/vfs_pread/vfs_write/vfs_pwrite接口的实现角度来分析如何在VFS层满足上述这三个硬性要求。

vfs_read接口实现

开启O_DIRECT情况下,vfs_read操作替换为以下操作:

off_t offset = vfs[vfdP->kind].vfs_lseek(vfdP->fd, (off_t)0, SEEK_CUR);
res = vfs_pread(file, buf, len, offset);
if (res > 0)
    vfs[vfdP->kind].vfs_lseek(vfdP->fd, (off_t)(offset + res), SEEK_SET);

复制

备注:根据vfs_pread的返回值,调整文件指针的偏移量。

vfs_pread接口实现

缓存地址对齐方法:新malloc出一段内存polar_directio_buffer,内存大小由参数_polar_max_direct_io_size_决定,默认1MB。

文件指针偏移量对齐方法:如图所示,原本的文件偏移范围是:[start, start+len],在directIO下,按照最小单位4KB,将将等待读取的文件内容范围划分为三部分:first section(范围[head_start, head_end])、middle section(范围[head_end, tail_start])、last section(范围[tail_start, tail_end])。其中,first section和last section的读取偏移量和读取大小分别调整为:head_start与4KB、tail_start与4KB,middle section的读取和原本pread的读取方式一样,最大的读取单位1MB(_polar_max_direct_io_size_)也保持一致。

文件读大小对齐方法:如图所示,文件读取的大小被划分为三类:第一类是first section和last section的读取单位4KB,第二类是middle section的读取单位1MB(_polar_max_direct_io_size_),第三类是middle section读取最后剩余的不足1MB的部分xKB(4KB的整数倍)。

image

vfs_write接口实现

开启O_DIRECT情况下,vfs_write操作替换为以下操作:

off_t offset = vfs[vfdP->kind].vfs_lseek(vfdP->fd, (off_t)0, SEEK_CUR);
res = vfs_pwrite(file, buf, len, offset);
if (res > 0)
    vfs[vfdP->kind].vfs_lseek(vfdP->fd, (off_t)(offset + res), SEEK_SET);

复制

备注:根据vfs_pwrite的返回值,调整文件指针的偏移量。

vfs_pwrite接口实现

缓存地址对齐方法:新malloc出一段内存polar_directio_buffer,内存大小由参数_polar_max_direct_io_size_决定,默认1MB。

文件指针偏移量对齐方法:如图所示,原本的文件偏移范围是:[start, start+len],在directIO下,按照最小单位4KB,将待写入内容的文件内容范围划分为三部分:first section(范围[head_start, head_end])、middle section(范围[head_end, tail_start])、last section(范围[tail_start, tail_end])。其中,first section和last section的写入偏移量和写入大小分别调整为:head_start与4KB、tail_start与4KB,middle section的写入和原本pwrite的写入方式一样,最大的写入单位1MB(_polar_max_direct_io_size_)也保持一致。

由于写入的时候,需要保证写入内容的大小是4KB的整数倍,因此,需要额外读取出first section和last section中缺少的那部分文件内容。

文件写大小对齐方法:如图所示,文件写入的大小被划分为三类:第一类是first section和last section的写入单位4KB,第二类是middle section的写入单位1MB(_polar_max_direct_io_size_),第三类是middle section写入最后剩余的不足1MB的部分xKB(4KB的整数倍)。

image

Direct I/O与Buffer I/O的不同

Direct I/O模块默认会将文件大小扩展为4KB的整数倍,因此,在(p)read接口读取到文件尾部的时候,Direct I/O模块读取到的内容一般是多于Buffer I/O读取的内容的,多余的部分是文件大小对齐导致的,多余的部分需要全部填充为0x0。

由于PolarDB PostgreSQL版本数据库中,数据页面的大小默认是8KB,因此大多数的文件大小都是满足4KB整数倍的要求的。但同时存在一些特殊的文件,其大小和内存中数据结构的大小一致,因此,这些文件需要在Direct I/O模块写操作完成时,检查是否需要将文件大小truncate成预期的大小,否则会影响上层代码中对该文件大小判断的相关逻辑。

Direct I/O使用

修改polar_datadir路径配置,添加“file-dio://”协议头,例如:

原本为:polar_datadir="original_path"

修改为:polar_datadir="file-dio://original_path"


文章转载自PolarDB,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论