当前位置:首页 > 技术知识 > 正文内容

从数据库启动日志看PostgreSQL的崩溃恢复

maynowei1周前 (08-07)技术知识9

作者:吴聪


背景


今天碰到朋友问我个问题,数据库启动时日志中的这个“invalid record length at 3/EAA68B8: wanted 24, got 0”里面的wanted 24是啥意思。



而数据库正常启动时的日志其实并不是上面那样,而是如下图所示:



上图的日志是因为数据库异常关闭后重启导致的,例如进程直接被kill。


为了搞清楚上面日志中的那条记录是啥意思,我们来一起研究下数据库异常关闭后恢复的过程。


崩溃恢复概述


首先我们要清楚数据库异常关闭和正常的停库有啥区别。当数据库异常关闭时,数据库的共享缓冲区中的数据还没有来得及刷到磁盘中,必然是会丢失,这个时候启动数据库便是从不正常的状态去启动,也就是我们要研究的崩溃恢复。


之前我也介绍过checkpoint相关的内容, 当PostgreSQL数据库崩溃恢复时,会以最近的checkpoint为基础,不断应用这之后的XLOG日志。为了更好地理解PostgreSQL数据库从崩溃中恢复的过程,我们需要弄清楚以下几个问题:


  • 数据库操作系统如何识别到自己是非正常状态(崩溃状态)
  • 数据库如何找到合适的checkpoint作为基础
  • 为什么应用XLOG日志可以恢复数据库数据
  • 数据库如何应用XLOG日志


数据库状态


在pg中数据库状态分为以下几种(可以通过控制文件查看数据库状态):


typedef enum DBState
{
    DB_STARTUP = 0,/*数据库启动*/
    DB_SHUTDOWNED,/*数据库正常关闭*/
    DB_SHUTDOWNED_IN_RECOVERY,/*数据库在恢复时关闭*/
    DB_SHUTDOWNING,/*数据库启动到正常关闭过程中崩溃*/
    DB_IN_CRASH_RECOVERY,/*数据库在恢复过程中崩溃*/
    DB_IN_ARCHIVE_RECOVERY,/*数据库处于归档恢复*/
    DB_IN_PRODUCTION/*数据库处于正常工作状态,等待接受事务处理*/
} DBState;


当数据库正常关闭时,数据库状态便是shut down,如果是异常关闭,可能会如下所示:

Database cluster state: in production


而每次当PostgreSQL数据库启动时,会首先读取控制文件获取数据库的状态,如果为非正常关闭状态,则会执行崩溃恢复逻辑。


chekpoint相关结构


当数据库进行崩溃恢复时,因为需要恢复异常关闭时丢失的共享内存中的数据,所以需要通过checkpoint来作为基础,不断的应用wal日志来恢复。而checkpoint相关的信息在pg中是存放在控制文件中的,由ControlFileData结构体存储:


typedef struct ControlFileData
{
        ...
    XLogRecPtr    checkPoint;        /*指向最近一次的检查点位置*/
    XLogRecPtr    prevCheckPoint;  /*指向最近一次检查点的前一次检查点的位置*/
    CheckPoint    checkPointCopy; /*最近一次检查点控制信息的副本*/
    XLogRecPtr    minRecoveryPoint; /*归档恢复时必须恢复到的最小LSN*/
    XLogRecPtr    backupStartPoint; /*在线备份时进行的检查点开始LSN*/
    XLogRecPtr    backupEndPoint; /*在线备份时进行的检查点结束LSN*/
        bool        backupEndRequired; /* 用于判断是否基于正确的在线备份集恢复*/
        TimeLineID    minRecoveryPointTLI; /* 必须恢复到的最小时间线 */
    ...
    pg_crc32    crc;
} ControlFileData;


例如下图所示:



一般来说都会选取最近一次的checkpoint作为恢复点,但是会存在一种情况:在执行最近一次的checkpoint是数据库异常关闭了,那么这个检查点可能是不完整的。因此一般还会多存储一个检查点的位置,即prevCheckPoint。


在数据库崩溃恢复过程中,PostgreSQL规定了三个在启动之前必须恢复到的最小位点:


  • minRecoveryPoint:

数据库在归档恢复过程中,minRecoveryPoint被更新为最新被刷新到磁盘的LSN。每次数据库启动时必须已经replay该位置的XLOG日志记录。

  • backupStartPoint:

数据库在线备份开始时,会调用pg_start_backup函数执行一次checkpoint,并生成backup_label文件。当使用在线备份集进行恢复时,backupStartPoint就是上述checkpoint记录对应的LSN,当达到了该LSN,该值置为0,在置为0之前,数据库不能启动。该值被记录在

backup_label文件中如下,直到在线备份结束,pg_stop_backup将该文件删除。这样就保证了在备份过程中,数据库崩溃了,可以默认从备份开始时的日志检查点开始恢复。

  • backupEndPoint:

当数据库从一个备库做的在线备份集进行恢复时,backupEndPoint表示备份结束的LSN,当达到该LSN,该值置为0,在置为0之前,数据库不能启动。


崩溃恢复具体过程:


每次postmaster进程启动时,都会调用StartupXLOG函数对数据库崩溃进行恢复。


其恢复过程大致为:


  1. 初始化内存,启动后台进程。
  2. pg在启动时读取pg_control文件内容。如果state为’in production’,PostgreSQL将进入恢复模式,因为这意味着数据库没有正常停止;如果为’shutdown’,将进入正常启动模式。
  3. pg从相应的WAL段文件中读取最新的检查点记录(位于pg_control文件中),并从记录中获取重做点。如果最新的检查点记录无效(invalid),pg将读取前一个检查点的记录。如果两个记录都不可读,将放弃恢复。注意,从11版本开始不会再存储前一个检查点的记录信息。
  4. 使用合适的资源管理器从重做点开始按顺序读取和重放WAL记录,直到最新WAL文件的最后位置。当遇到备份块时,无论其LSN如何,都会将覆盖相应表的页面。否则仅当此wal记录LSN>相应页面的pd_lsn时,才会重放该WAL记录。


崩溃恢复日志


介绍了这么多,我们再来看看为什么在崩溃恢复时数据库的日志中输出内容是那样的。


因为检测到数据库为异常状态,所以需要从最近一次检查点开始按顺序读取wal日志:



当检测到没有wal日志可以重放时,便会抛出前面的提示:“invalid record length at 3/EAA6580: wanted 24, got 0”。那你可能要问为啥为wanted 24,不是25,26。。。


那我们接着看:



显然这里的24就是SizeOfXLogRecord,其计算方法如下:



这里需要注意,XLogRecord结构体中有2bytes的填充位。


总结


一个简单的数据库启动日志竟然包含着这么多知识!


参考链接:


https://www.pgcon.org/2012/schedule/attachments/258_212_Internals%20Of%20PostgreSQL%20Wal.pdf

http://mysql.taobao.org/monthly/2017/05/03/

src/backend/access/transam/xlog.c

src/backend/access/transam/xlogreader.c

相关文章

分享我的产品策划流程,希望对你也有用

本文笔者梳理拆解了自己的产品策划流程,并给出了自己对各流程的思考,希望能够给你带来一定的启发。记得刚开始做产品出需求方案的时候,上来就开始画原型写文档,在写的过程中发现某个交互没想明白或者漏了一部分逻...

从零搭建网站?5个步骤详解网站建设全流程

在数字化时代,拥有一个专业网站已成为企业或个人品牌展示的核心竞争力。但对于新手而言,网站建设往往显得复杂难懂。本文梳理出5大关键步骤,手把手带你完成从策划到上线的全流程,省时省力不踩坑!一、前期准备:...

Win10系统除了移植Objective C,还要有Swift

IT之家讯Build2015大会最让人震惊的莫属微软推出工具,Win10系统可以移植安卓/iOS应用,特别是后者使用的Objective C编程语言,不过最新消息表明,微软连苹果最新的Swift移植也...

[三菱PLC] 用"C语言"玩转PLC,三菱PLC使用ST语言超详细教程

ST语言,全称为结构化文本(Structured Text),是一种高级编程语言,专为工业自动化和控制系统设计。我们学习PLC一般是用梯形图,梯形图学会后,学习SFC,但是我发现梯形图和SFC虽然简单...

C++并发同步核心-mutex深度解析:守护共享数据的关键

在多线程编程中,当多个线程需要访问和修改共享数据时,如果没有任何同步机制,就可能发生数据竞争(Data Race),导致程序行为不可预测、数据损坏甚至崩溃。C++标准库通过<mutex>头...

C语言进阶教程:线程同步:互斥锁、条件变量与信号量

在多线程编程中,线程同步是确保数据一致性和程序正确性的关键。当多个线程需要访问共享资源时,如果缺乏适当的同步机制,就可能导致竞态条件(Race Condition)、死锁(Deadlock)等问题。本...