Buffile.c
文件是 PostgreSQL 存储系统中的一个组成部分,专注于管理和优化临时文件的读写操作。它提供了一种对虚拟文件(由 fd.c
管理)的缓冲输入/输出(I/O)支持,以提高性能并减少资源消耗。
一、BufFile结构
当BufFile
被创建时,数据结构被存储在palloc
中。BufFile
同样支持超过OS文件大小限制的临时文件。BufFile
还支持临时文件,当相应文件需要在整个事务中存活并多次打开和关闭时,单个后端可以使用这些临时文件。此类文件需要作为 FileSet
的成员。另外无论RELSEG_SIZE
大小如何,都会将BufFile
分成千兆字节大小的段。原因是希望大的BufFile
在可用的情况下分散到多个表空间。
二、功能
1、缓冲I/O
BufFile
提供了一种类似标准 C 库中stdio
的缓冲 I/O 功能。这意味着读写操作首先会在内存中的缓冲区进行,直到缓冲区满或空时,才会对底层文件进行实际的读写操作。这减少了直接对底层文件的访问次数,特别是对于虚拟文件(由fd.c
管理),可以减少文件描述符的打开和关闭次数,从而提高性能。
2、大文件支持
BufFile
支持处理超过操作系统文件大小限制的大文件。这是通过打开多个fd.c
管理的临时文件来实现的,当一个文件达到其大小限制时,会自动切换到另一个文件继续写入。这对于处理大量数据的排序和哈希连接操作特别重要。
3、资源共享
BufFile
支持创建可以与其他后端进程共享的临时文件。这是通过将文件作为共享文件集(SharedFileSet
)的成员来实现的,使得参与并行执行的不同进程可以访问相同的文件。
4、跨事务生存能力
- 对于需要在单个后端中跨事务持久化并且需要多次打开和关闭的临时文件,
BufFile
支持将这些文件作为文件集(FileSet
)的成员来创建。这样,即使在事务之间,这些文件也可以保持打开状态或根据需要重新打开。
三、源码解读
1、BufFile
结构体
BufFile
是PostgreSQL中用于表示一个缓冲文件的数据结构,这个缓冲文件可能由一个或多个物理文件组成,每个物理文件都通过一个由fd.c管理的虚拟文件描述符进行访问。这个结构体主要用于管理临时文件,特别是那些需要频繁读写且数据量可能超过单个文件大小限制的场景。
struct BufFile
{
int numFiles; /* 文件集中物理文件的数量 */
File *files; /* 指向 File 类型的动态分配(使用 palloc)数组的指针 */
bool isInterXact; /* 锁,是否保持事务打开 */
bool dirty; /* 缓冲是否需要被写入,是否脏块 */
bool readOnly; /* 文件是否设置为只读 */
FileSet *fileset; /* 指向 FileSet 结构体的指针 */
const char *name; /* 指向字符数组的指针,表示基于文件集的 BufFile 的名称 */
ResourceOwner resowner; /* 用于管理底层临时文件的资源所有者 */
int curFile; /* 文件起始位置的索引 */
off_t curOffset; /* 起始位置的偏移 */
int pos; /* 下一个缓冲区读/写位置 */
int nbytes; /* 缓冲区总有效字节数,数据总数 */
PGAlignedBlock buffer;
};
2、创建BufFile
实例
static BufFile * makeBufFileCommon(int nfiles)
{
BufFile *file = (BufFile *) palloc(sizeof(BufFile)); /* 动态分配内存,并讲分配的内存地址转换为`BufFile*`类型,并赋值给`file`指针 */
file->numFiles = nfiles;
file->isInterXact = false;
file->dirty = false;
file->resowner = CurrentResourceOwner;
file->curFile = 0;
file->curOffset = 0L;
file->pos = 0;
file->nbytes = 0;
return file;
}
- 首先使用
palloc
函数动态分配BufFile
结构体所需的内存 - 初始化
BufFile
结构体的各个成员
3、根据第一个底层物理文件构造BufFile
static BufFile * makeBufFile(File firstfile)
{
BufFile *file = makeBufFileCommon(1);
file->files = (File *) palloc(sizeof(File));
file->files[0] = firstfile;
file->readOnly = false;
file->fileset = NULL;
file->name = NULL;
return file;
}
- 使用
makeBufFileCommon
函数构造只包含一个文件file的BufFile
- 为file分配空间,
BufFile
指针指向file - 设置结构体中其他成员
4、添加另一个组件临时文件
static void extendBufFile(BufFile *file)
{
File pfile;
ResourceOwner oldowner;
oldowner = CurrentResourceOwner;
CurrentResourceOwner = file->resowner;
if (file->fileset == NULL)
pfile = OpenTemporaryFile(file->isInterXact); /* 打开临时文件,关闭后文件消失 */
else
pfile = MakeNewFileSetSegment(file, file->numFiles); /* 创建一个新的段文件,支持基于BufFile的文件集 */
Assert(pfile >= 0);
CurrentResourceOwner = oldowner;
file->files = (File *) repalloc(file->files, (file->numFiles + 1) * sizeof(File)); /* 重新分配内存块,更改所指向的内存块的大小。 */
file->files[file->numFiles] = pfile;
file->numFiles++;
}
-
资源所有者切换:
- 首先,函数保存当前的活动资源所有者(
CurrentResourceOwner
),并将它设置为BufFile
结构体中指定的资源所有者(file->resowner
)。这是为了确保与BufFile
相关联的临时文件能够正确地与特定的资源所有者(如事务或会话)相关联,从而能够在需要时正确地清理这些文件。
- 首先,函数保存当前的活动资源所有者(
-
打开新文件:
- 如果
file->fileset
为NULL
,表示这是第一次扩展文件集合,那么它将调用OpenTemporaryFile
函数来创建一个新的临时文件,并根据file->isInterXact
的值(指示是否跨事务)来设置文件的属性。 - 如果
file->fileset
不为NULL
,则表示已经有一个文件集合存在,此时将调用MakeNewFileSetSegment
函数来为该集合添加一个新的文件段。
- 如果
-
断言文件句柄有效:
- 使用
Assert(pfile >= 0);
来确保返回的文件句柄是有效的(在 Unix-like 系统中,有效的文件句柄是非负的)。
- 使用
-
恢复资源所有者:
- 将当前的活动资源所有者恢复为之前保存的值,以确保后续操作不受此函数的影响。
-
扩展文件句柄数组:
- 使用
repalloc
函数重新分配file->files
数组,以便能够存储更多的文件句柄。新的数组大小为file->numFiles + 1
,即当前文件数量加一。 - 将新打开的文件句柄添加到数组的新位置。
- 更新
file->numFiles
以反映文件数量的增加。
- 使用