更多"C/C++、PostgreSQL、编译原理、计算机原理、TCP/IP、数据结构&算法、Linux编程”等技术文章更新于公众号: 君子黎


1. 简述postmaster.pid文件与pg_control文件间区别

     我们曾在 postmaster.pid文件都存储了什么 详谈pg_control文件的作用 两文中对各自文件的内容以及内部原理做了比较详细的描述。但是,对这两个文件之间的差异,在该系列文章中还没有进进行讲解过,因此,本文将讲述它们间的区别,以及各自对整个PostgreSQL系统服务的影响和约束,同时重点剖析若运行中的PostgreSQL服务遇到postmaster.pid文件被删除或是文件内容被修改会发生声明?

     首先可以肯定的是,这两个文件对于PostgreSQL数据库来说,都是至关重要的。但是它们之间还是有着很大的差异,即各自负责的功能不同,所谓各司其职。对于pg_control,它是PostgreSQL数据库的控制文件,是在第一次安装数据库(initdb)时候生成的,文件中存储的是当前数据库目录(PGDATA)的版本、初始化配置参数、系统唯一标识、CRC校验码等等,该文件是在initdb之后就一直存在的,PostgreSQL服务的启停不影响该文件的存在状态,也就是说这个文件只要该PGDATA数据蔟目录是有效的,那么就会一直存在。如果这个文件被删除,那么PostgreSQL数据库服务就永远起不来了。它保证了数据库的安全,同时通过读取该文件内容并进行逻辑校验,能保证不同版本的程序命令无法进行非法操作。

     而postmaster.pid文件则不一样,它是随着PostgreSQL数据库服务的每次启动而创建,停止而被删除,也就是说,该文件的存在与否永远受到服务状态的影响。该文件的读取是在pg_control文件之后,而且该文件中存储的数据和系统的配置参数这些没有关系,它只记录当前启动的postmaster服务的进行PID、进程启动时间戳、数据蔟目录位置、socket套接字目录位置、共享内存key等信息,当PostgreSQL被停止后,该文件将立刻被删除,从而保证下一次的PostgreSQL服务能够正常启动。

     pg_control是PostgreSQL服务能够启动的必要条件。

2. PostgreSQL服务的正常运行离不开postmaster.pid文件

     当PostgreSQL服务正常启动之后,会在ServerLoop()函数中负责管理各后端辅助进程、后台进程以及和系统有关的控制文件pg_control、锁文件postmaster.pid,以及等待接收客户端的连接处理等等。该函数内部是死循环,只要PostgreSQL服务运行正常,则该函数将永远不会返回;若返回,则表明当前服务出现了异常情况,默认采取的措施是立刻停止当前的PostgreSQL服务。

     在ServerLoop()函数中,会每隔1分钟去检测一次postmaster.pid文件的状态,以确认该文件没有被覆盖或是被删除。

#define SECS_PER_MINUTE 60

for( ; ; )
{
    // . . . . . .  省略其余部分代码
    if (now - last_lockfile_recheck_time >= 1 * SECS_PER_MINUTE)
    {
        if (!RecheckDataDirLockFile())
        {
            ereport(LOG,
                    (errmsg("performing immediate shutdown because data directory lock file is invalid")));
            kill(MyProcPid, SIGQUIT);
        }
        last_lockfile_recheck_time = now;
    }
    //. . . . . . 省略其余部分代码
}

     如果有,则立刻强制关闭服务。这样就避免了postmaster守护进程和子进程在它们的数据库消失之后仍然挂起的问题。如果在相同的地方创建(initdb)新的数据库集群,可能会导致该问题。同时还提供了一些保护,即防止数据库管理员(Database Administrator, DBA)不理智地去删除postmaster.pid文件并手动启动一个新的postmaster。

     那么这里会有两种情况,第一种是直接删除postmaster.pid文件;第二种是修改该文件中的内容,但是文件依然存在。下面将分别详细进行讲解这两种情形下的PostgreSQL数据库服务的采取措施是什么。

2.1 postmaster.pid文件被删除

     对应postmaster.pid文件的检测,主要由RecheckDataDirLockFile()函数负责处理。首先函数尝试打开该文件,若成功,则继续下面的流程判断处理;反之,若文件打开失败,则对open()函数返回的错误码进行判断, 若该错误码是ENOTDIR,则立刻停止该数据库服务。若为其他错误码,则继续下一次的检测逻辑。其部分代码如下:

bool
RecheckDataDirLockFile(void)
{
    int            fd;
    int            len;
    long        file_pid;
    char        buffer[BLCKSZ];

    fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
    if (fd < 0)
    {
        switch (errno)
        {
            case ENOENT:
            case ENOTDIR:
                /* disaster */
                ereport(LOG,
                        (errcode_for_file_access(),
                         errmsg("could not open file \"%s\": %m",
                                DIRECTORY_LOCK_FILE)));
                return false;
            default:
                /* non-fatal, at least for now */
                ereport(LOG,
                        (errcode_for_file_access(),
                         errmsg("could not open file \"%s\": %m; continuing anyway",
                                DIRECTORY_LOCK_FILE)));
                return true;
        }
    }
    // . . . . . . 省略若干业务代码
}

     附:ENOTDIR(open)
     不是一个目录。给定的路径虽然存在,但不是一个目录。

     其具体内部业务逻辑处理流程图如下所示:
在这里插入图片描述

     结论:若运行中的PostgreSQL服务,当检测到postmaster.pid文件不存在时,则立刻停止服务。如下图所示:

在这里插入图片描述

2.2 postmaster.pid文件内容被修改

     从2.1节中指导,若postmaster.pid文件被删除掉,则服务将在本次检测时候,立刻停止运行中的postmaster服务。 那么postmaster.pid文件不被删除,但是修改了其中的内容又会怎样呢? 下面我们将继续分析这个问题。

     这部分的业务代码如下所示:

bool
RecheckDataDirLockFile(void)
{
     // . . . . . . 省略若干业务代码

    len = read(fd, buffer, sizeof(buffer) - 1);
    pgstat_report_wait_end();
    if (len < 0)
    {
        ereport(LOG,
                (errcode_for_file_access(),
                 errmsg("could not read from file \"%s\": %m",
                        DIRECTORY_LOCK_FILE)));
        close(fd);
        return true;            /* treat read failure as nonfatal */
    }

    buffer[len] = '\0';
    close(fd);
    file_pid = atol(buffer);
    if (file_pid == getpid())
        return true;            /* all is well */

    /* Trouble: someone's overwritten the lock file */
    ereport(LOG,
            (errmsg("lock file \"%s\" contains wrong PID: %ld instead of %ld",
                    DIRECTORY_LOCK_FILE, file_pid, (long) getpid())));
    return false;
}

    当文件成功打开之后,将返回一个指向当前内核中可用的最小文件描述符fd。然后从中读取一行数据,我们在 postmaster.pid文件都存储了什么 中介绍过,postmaster.pid文件中的第一行数据是当前运行的postmaster守护进程的PID。

在这里插入图片描述

    因此通过判断该PID的值和getpid()函数获取的结果进行比对即可得知这个PostgreSQL服务是否正常。若正常,则getpid()和从postmaster.pid文件中获取的PID值相同,反之,则不相同。所以若该文件中,除第一行数据外,其余行数据被修改或是删除时,PostgreSQL服务仍然能够运行;反之若第一行数据被修改,那么服务也会立刻停止运行。其内部逻辑处理流程图如下所示:

在这里插入图片描述

     当修改了第一行数据之后,PostgreSQL服务立刻停止并打印相关的日志信息。

在这里插入图片描述

3. 总结

     本文比较详细地介绍了pg_control控制文件与postmaster.pid锁文件两者之间的区别,包括但不限于文件的生成时间、生命周期、规则以及文件大致中大致内容。同时对postmaster.pid锁文件的存在与否,以及文件中内容修改、怎么修改对PostgreSQL服务造成的不同影响进行了详细分析说明。通过文字、代码并结合流程图的方式,使文章阅读起来的效率事半功倍。