我们知道, 绝大部分数据库引擎都采用了WAL即Write-Ahead-Logging技术来保证数据库在断电、宕机等场景仍然保持数据的完整性和一致性,PostgreSQL也不例外, 但是我们可以思考一个问题, PostgresSQL在重启时怎么知道自己曾经发生过故障呢?需要WAL重放时又该从哪个位置开始进行呢? 本小节我们则来重点分析下这个问题。
在我们的$PG_DATA/global目录下存在着一个名为pg_control的文件,这个文件十分重要, 他是PG的核心控制文件,
它存储了PG服务器内部信息状态的各方面信息,比如最新检查点(checkpoint)、系统状态、当前运行的postgres服务版本、CRC校验,以及initdb初始化PG数据库蔟时设置的某些基本参数。
文件中保存的是结构体struct ControlFileData的内容,结构体定义如下:
// src/include/catalog/pg_control.h
typedef struct ControlFileData
{
uint64 system_identifier;
uint32 pg_control_version; /* PG_CONTROL_VERSION */
uint32 catalog_version_no; /* see catversion.h */
DBState state; /* DB的状态 */
/* 中间省略大量无关字段 */
/* CRC of all above ... MUST BE LAST! */
pg_crc32c crc;
} ControlFileData;
在这里我们只需要关注其中的DBState state
这个字段,DBState是一个枚举类型, 下面是它的定义:
// src/include/catalog/pg_control.h
typedef enum DBState
{
DB_STARTUP = 0, // PG正在启动(未使用)
DB_SHUTDOWNED, // PG正常关闭
DB_SHUTDOWNED_IN_RECOVERY, // PG在恢复时关闭
DB_SHUTDOWNING, // PG数据库启动到正常关闭过程中崩溃
DB_IN_CRASH_RECOVERY, // PG在恢复时CRASH
DB_IN_ARCHIVE_RECOVERY, // PG在归档恢复中
DB_IN_PRODUCTION // PG在正常运行中
} DBState;
pg_control文件采用二进制编码,我们无法通过一些简单的命令如cat查看其内容, 好在Postgres提供了一个工具pg_controldata帮助我们查看pg_control这个文件的内容, pg_controldata工具的使用方法如下
pg_controldata -D $PG_DATA
// pg_controldata工具源码位于src/bin/pg_controldata/pg_controldata.c,其实就是去
// 读取pg_control的内容并打印到标准输出,有兴趣的朋友可以阅读下代码。
我们重点关注其中的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则会开始恢复流程。
// src/backend/access/transam/xlog.c
// void StartupXLOG(void)