我们知道, 绝大部分数据库引擎都采用了WAL即Write-Ahead-Logging技术来保证数据库在断电、宕机等场景仍然保持数据的完整性和一致性,PostgreSQL也不例外, 但是我们可以思考一个问题, PostgresSQL在重启时怎么知道自己曾经发生过故障呢?需要WAL重放时又该从哪个位置开始进行呢? 本小节我们则来重点分析下这个问题。

    在我们的$PG_DATA/global目录下存在着一个名为pg_control的文件,这个文件十分重要, 他是PG的核心控制文件,
    它存储了PG服务器内部信息状态的各方面信息,比如最新检查点(checkpoint)、系统状态、当前运行的postgres服务版本、CRC校验,以及initdb初始化PG数据库蔟时设置的某些基本参数。
    image.png
    文件中保存的是结构体struct ControlFileData的内容,结构体定义如下:

    1. // src/include/catalog/pg_control.h
    2. typedef struct ControlFileData
    3. {
    4. uint64 system_identifier;
    5. uint32 pg_control_version; /* PG_CONTROL_VERSION */
    6. uint32 catalog_version_no; /* see catversion.h */
    7. DBState state; /* DB的状态 */
    8. /* 中间省略大量无关字段 */
    9. /* CRC of all above ... MUST BE LAST! */
    10. pg_crc32c crc;
    11. } ControlFileData;

    在这里我们只需要关注其中的DBState state这个字段,DBState是一个枚举类型, 下面是它的定义:

    1. // src/include/catalog/pg_control.h
    2. typedef enum DBState
    3. {
    4. DB_STARTUP = 0, // PG正在启动(未使用)
    5. DB_SHUTDOWNED, // PG正常关闭
    6. DB_SHUTDOWNED_IN_RECOVERY, // PG在恢复时关闭
    7. DB_SHUTDOWNING, // PG数据库启动到正常关闭过程中崩溃
    8. DB_IN_CRASH_RECOVERY, // PG在恢复时CRASH
    9. DB_IN_ARCHIVE_RECOVERY, // PG在归档恢复中
    10. DB_IN_PRODUCTION // PG在正常运行中
    11. } DBState;

    pg_control文件采用二进制编码,我们无法通过一些简单的命令如cat查看其内容, 好在Postgres提供了一个工具pg_controldata帮助我们查看pg_control这个文件的内容, pg_controldata工具的使用方法如下

    1. pg_controldata -D $PG_DATA
    2. // pg_controldata工具源码位于src/bin/pg_controldata/pg_controldata.c,其实就是去
    3. // 读取pg_control的内容并打印到标准输出,有兴趣的朋友可以阅读下代码。

    image.png
    我们重点关注其中的Database cluster state这个字段, 其值为”in production”,刚好对应 enum DBState枚举中的
    DB_IN_PRODUCTION状态, 这说明我的PG服务正在正常运行中。

    回到我们的第一个问题, PG在故障后重新启动时, 会进行WAL重放来恢复数据, 但是PG是怎么知道自己曾经发生故障呢? 通过上面的分析, 想必有些朋友已经能想到答案了。没错, PG在每次启动时会去检查pg_control文件中的DBState,只要DBState不是DB_SHUTDOWNED或者DB_SHUTDOWNED_IN_RECOVERY状态,则认为系统是非正常关闭的, 那么PG则会开始恢复流程。

    1. // src/backend/access/transam/xlog.c
    2. // void StartupXLOG(void)