概述
He3DB for PostgreSQL中的文件管理是一个复杂但关键的部分,它涉及到数据库文件的存储、组织、维护和备份等多个方面。He3DB for PostgreSQL的文件系统主要由几个关键目录和文件组成,这些文件和目录在数据库的安装和运行过程中起着至关重要的作用,因此需要管理员具备丰富的知识和经验来管理数据库中的文件,以确保数据库的稳定运行和高效性能。
1. 初始化打开文件
void InitFileAccess(void)
{
Assert(SizeVfdCache == 0); /* call me only once */
/* initialize cache header entry */
VfdCache = (Vfd *) malloc(sizeof(Vfd));
if (VfdCache == NULL)
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
MemSet((char *) &(VfdCache[0]), 0, sizeof(Vfd));
VfdCache->fd = VFD_CLOSED;
SizeVfdCache = 1;
}
1.1 函数功能
PostgreSQL数据库的后端启动时,初始化文件访问模块。
- 该函数特别关注于初始化虚拟文件描述符缓存,该缓存用于管理数据库中的文件打开操作。注意,这个函数只在正常的后端启动或独立后端启动时调用,而在postmaster(主服务器进程)中不会被调用。
1.2 源码解析
1、断言Assert
检查:
- 函数首先通过一个断言
Assert
检查SizeVfdCache
是否为0,以确保该函数只被调用一次。SizeVfdCache
变量用于记录VFD缓存中已分配的条目数。
2、初始化 VFD 缓存:
- 使用malloc分配内存以存储VFD缓存的第一个条目。如果内存分配失败,错误代码为
ERRCODE_OUT_OF_MEMORY
,并附带相应的错误消息。 - 使用MemSet函数将分配的内存区域清零,确保VFD缓存的第一个条目是干净的。这里特别对VfdCache[0]进行清零,虽然由于malloc已经分配了足够的内存,这一步在理论上是多余的(因为malloc分配的内存区域初始化为未定义值,但通常表现为零或随机值),但它增强了代码的清晰度和健壮性。
- 将VfdCache->fd设置为
VFD_CLOSED
,表示这个VFD初始时是关闭的。
3、新 VFD 缓存大小:
- 将
SizeVfdCache
设置为 1,表示VFD缓存中现在有一个条目。
2. 打开文件
static int FileAccess(File file)
{
int returnValue;
DO_DB(elog(LOG, "FileAccess %d (%s)",
file, VfdCache[file].fileName));
if (FileIsNotOpen(file)) // (VfdCache[file].fd == VFD_CLOSED)
{
returnValue = LruInsert(file);
if (returnValue != 0)
return returnValue;
}
else if (VfdCache[0].lruLessRecently != file)
{
Delete(file);
Insert(file);
}
return 0;
}
2.1 函数功能
访问文件
- 在一个基于LRU缓存策略的文件管理系统中管理文件的访问。
2.2 源码解析
1、日志记录
- 使用
DO_DB
宏记录一条日志,表明正在访问的文件编号(file
)和对应的文件名(VfdCache[file].fileName
)
2、检查文件是否已打开
- 通过调用
FileIsNotOpen(file)
函数来判断指定的文件是否已经打开。 - 若文件未打开,调用
LruInsert(file)
函数尝试将文件插入到LRU缓存中。如果LruInsert
返回非零值,表示插入失败或发生错误,则函数直接返回该错误码。
3、更新已打开文件的LRU状态
- 若文件已打开,但该文件不在头部,则从当前位置删除文件的记录(调用
Delete(file)
),然后将其重新插入到LRU缓存的头部(调用Insert(file)
)。
4、返回值
- 如果函数执行到这里没有返回错误码,则函数返回0,表示成功完成文件访问的更新操作。
3. 关闭文件
void FileClose(File file)
{
Vfd *vfdP;
Assert(FileIsValid(file));
DO_DB(elog(LOG, "FileClose: %d (%s)",
file, VfdCache[file].fileName));
vfdP = &VfdCache[file];
if (!FileIsNotOpen(file))
{
/* close the file */
if (close(vfdP->fd) != 0)
{
elog(vfdP->fdstate & FD_TEMP_FILE_LIMIT ? LOG : data_sync_elevel(LOG),
"could not close file \"%s\": %m", vfdP->fileName);
}
--nfile;
vfdP->fd = VFD_CLOSED;
/* remove the file from the lru ring */
Delete(file);
}
if (vfdP->fdstate & FD_TEMP_FILE_LIMIT)
{
temporary_files_size -= vfdP->fileSize;
vfdP->fileSize = 0;
}
if (vfdP->fdstate & FD_DELETE_AT_CLOSE)
{
struct stat filestats;
int stat_errno;
vfdP->fdstate &= ~FD_DELETE_AT_CLOSE;
/* first try the stat() */
if (stat(vfdP->fileName, &filestats))
stat_errno = errno;
else
stat_errno = 0;
/* in any case do the unlink */
if (unlink(vfdP->fileName))
ereport(LOG,
(errcode_for_file_access(),
errmsg("could not delete file \"%s\": %m", vfdP->fileName)));
/* and last report the stat results */
if (stat_errno == 0)
ReportTemporaryFileUsage(vfdP->fileName, filestats.st_size);
else
{
errno = stat_errno;
ereport(LOG,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m", vfdP->fileName)));
}
}
/* Unregister it from the resource owner */
if (vfdP->resowner)
ResourceOwnerForgetFile(vfdP->resowner, file);
FreeVfd(file);
}
3.1 函数功能
关闭文件
- 用于关闭一个已经打开的文件,并根据文件的属性执行一些清理工作
3.2 源码解析
1、断言Assert
检查:
- 使用
Assert(FileIsValid(file));
确保传入的文件标识符是有效的
2、日志记录
- 使用
DO_DB
宏记录一条日志,记录关闭文件的操作,包括文件标识符和文件名。
3、文件关闭
- 使用
vfdP = &VfdCache[file];
获取文件描述符的指针。 - 检查文件是否确实已经打开(
!FileIsNotOpen(file)
),如果是,则调用close(vfdP->fd)
来关闭文件。如果关闭失败,会根据文件是否为临时文件来决定是否记录错误日志或触发更严重的错误处理。 - 更新全局的文件计数器
nfile
,并将文件描述符设置为VFD_CLOSED
以表示文件已关闭。 - 从LRU中删除该文件描述符。
4、临时文件处理
- 如果文件是临时文件(
vfdP->fdstate & FD_TEMP_FILE_LIMIT
),则从临时文件大小中减去该文件的大小。 - 如果设置了
FD_DELETE_AT_CLOSE
标志,则在关闭文件后尝试删除该文件,并在删除前后记录相关信息(如文件大小或删除失败的原因)。
5、资源所有者注销
- 如果文件与一个资源所有者相关联(
vfdP->resowner
),则从资源所有者中注销该文件,以便资源清理时可以正确处理。
6、释放vfd
- 通过
FreeVfd(file);
将文件描述符的槽位释放回空闲列表
作者介绍
殷子婷 移动云数据库工程师,负责云原生数据库He3DB的研发