`

PostgreSQL启动过程中的那些事十六:启动进程二

阅读更多

 

       这节主要讨论启动进程到了 StartupXLOG 。根据情况,如果需要就排除系统故障引起的数据库不一致状态,做相应的 REDO UNDO ,然后创建一个检查点,把所有共享内存磁盘缓冲和提交数据缓冲写并文件同步到磁盘、把检查点插入 xlog 文件、更新控制文件,使数据库达到一种状态,设置共享内存中 XLogCtl ShmemVariableCache 等对象信息 ;如果不需要,就根据控制文件从 xlog 文件读取最后的检查点信息,设置共享内存中 XLogCtl ShmemVariableCache 等对象信息;启动完 XLOG ,启动进程完成使命,自己做了了断, postmaster 进程根据子进程结束信号响应句柄继续。

目前没有看到数据文件里记录了检查点,难道这个没有???

 

3

   先上个图

 


 

方法调用序列示意图

 

4

StartupXLog 方法的处理流程示意图

 

 

 

StartupXLog 流程示意图

 

数据库在什么情况下需要恢复。如果出现事务故障、系统故障或者介质故障时,数据库需要恢复。出现诸如运算溢出、死锁、违反完整性约束等事务故障时数据库在运行时可以通过强制回滚自行处理。 系统故障是主存数据丢失,未完成事务有些数据已写到物理数据库,此时数据库启动时需要事务回滚( UNDO/rollback )恢复。已完成事务有些或全部数据未写到物理数据库,此时数据库启动时需要重做( REDO )已提交事务。介质故障,需要从以前的备份做专门恢复。

话说 到了XLogStartup 方法,在这个方法里最主要的是根据情况判断是否需要恢复。如果不需要恢复,处理比较简单;如果需要恢复,根据不同状况做相应恢复。这儿这个状况就是判断系统是否在上次关闭时出现了系统故障。

XLogStartup 方法,先读取控制文件pg_control ControlFileData 数据结构,再看是否有恢复命令文件recovery.con f (判断是否是归档模式,如果归档模式,需要恢复的话,就是归档恢复),读取内容并 设置 InArchiveRecovery = true 并根据情况 如果是 hot standby 的从系统 设置 StandbyMode = true 。接着读时间线历史文件。 然后 把来自控制文件的恢复目标时间线 recoveryTargetTLI 和归档清楚命令 archive_cleanup_command 保存于共享内存中的 xlog 控制结构 XLogCtl 的相关成员里 以备其它进程查看。 接着 调用 read_backup_label 方法 看是否有备份标签文件 $PGDATA$/backup_label ,如果有, 从中取检查点记录位置赋给 checkPointLoc ,如果没有备份标签文件,把控制文件里的检查点位置赋给 checkPointLoc 。根据这个检查点位置指针 checkPointLoc xlog 文件中读这个检查点的 xlog 记录解析到检查点对象 checkpoint 。根据其自身位置指针和其记录的下一个 xlog 记录位置指针,或者控制文件记录的 数据库是否非正常关闭状态或 ControlFile->state != DB_SHUTDOWNED 者有无 recover.conf 文件, 判断是否需要恢复。

如果不需要恢复,更新控制文件的 state 等于 DB_IN_PRODUCTION time 等于系统当前时间。接着 设置共享内存里的 XLogCtl 的成员 Write.lastSegSwitchTime 为当前时间,根据控制文件初始化XLogCtl 的最后的检查点的 XID/epoch 再初始化共享内存里缓存变量结构 ShmemVariableCache latestCompletedXid 以备份事务 ID ShmemVariableCache->latestCompletedXid = ShmemVariableCache->nextXid 然后调用 RecoverPreparedTransactions() 扫描 pg_twophase 文件夹 重新为 准备 的事务加载共享内存状态。如果任何关键 GUC 参数改变了,在我们允许 backend 进程写 WAL 日志以前记录到日志。所有这些事搞定后,设置 xlogctl->SharedRecoveryInProgress = false 允许 backend 进程写 WAL 日志。然后退出启动进程,postmaster 进程响应子进程退出信号其它相关进程。

如果需要恢复。这儿要处理的是崩溃时刻未完成 事务已写入 物理数据库的事务(处理方法是 UNDO )和崩溃时刻已完成 事务未写入 物理数据库的事务(处理方法是 REDO )。

根据数据库的运行模式(有无起归档,有无 hot standby 。有 hot standby 时,其主系统恢复和启归档情况一样进行恢复,从系统单独处理),调用 ReadRecord 方法从不同地方读取 xlog 日志记录,调用 xlog 的资源管理器 xmgr 的相应资源的重放方法做恢复。恢复完成后剩余步骤和不需要恢复的情况一样,处理后续事宜。 然后退出启动进程,postmaster 进程响应子进程退出信号其它相关进程。略详细过程见“ XLogStartup 流程示意图”

相关主要结构见下面:

       控制文件的结构 ControlFileData 及检查点结构 CheckPoint 参见《 PostgreSQL 存储系统一:控制文件存储结构》。

XLog 日志文件相关结构参见《 PostgreSQL 存储系统二: REDOLOG 文件存储结构》。

 

VariableCache 是共享内存里用来跟踪OIDXID 分配状态的数据结构。由于历史原因,由不同的轻量锁LWLock 保护这个结构中不同的字段。

typedef struct VariableCacheData

{

    /* 这些字段由 OidGenLock 锁保护 */

    Oid         nextOid ;      /* next OID to assign */

    uint32      oidCount ;     /* OIDs available before must do XLOG work */

 

    /* 这些字段由 XidGenLock 锁保护 */

    TransactionId nextXid ;      /* next XID to assign */

 

    TransactionId oldestXid ; /* cluster-wide minimum datfrozenxid */

    TransactionId xidVacLimit ;  /* start forcing autovacuums here */

    TransactionId xidWarnLimit ; /* start complaining here */

    TransactionId xidStopLimit ; /* refuse to advance nextXid beyond here */

    TransactionId xidWrapLimit ; /* where the world ends */

    Oid         oldestXidDB ;  /* database with minimum datfrozenxid */

 

    /* 这些字段由 ProcArrayLock 锁保护 */

    TransactionId latestCompletedXid ;  /* newest XID that has committed or

                                     * aborted */

} VariableCacheData ;

 

typedef VariableCacheData * VariableCache ;

 

 

XLOG 的共享内存总状态

typedef struct XLogCtlData

{

    /* WALInsertLock 锁保护 */

    XLogCtlInsert Insert;

 

    /* info_lck 锁保护 */

    XLogwrtRqst LogwrtRqst;

    XLogwrtResult LogwrtResult;

    uint32     ckptXidEpoch; /* nextXID & epoch of latest checkpoint */

    TransactionId ckptXid;

    XLogRecPtr asyncXactLSN; /* LSN of newest async commit/abort */

    uint32     lastRemovedLog; /* latest removed/recycled XLOG segment */

    uint32     lastRemovedSeg;

 

    /* WALWriteLock 锁保护 */

    XLogCtlWrite Write;

 

    /* 尽管这些值可以变,但在启动后不再改变。是否可以读 / 写页面和块的值依赖于 WALInsertLock WALWriteLock */

    char       *pages;        /* buffers for unwritten XLOG pages */

    XLogRecPtr *xlblocks;       /* 1st byte ptr-s + XLOG_BLCKSZ */

    int         XLogCacheBlck;    /* highest allocated xlog buffer index */

    TimeLineID ThisTimeLineID;

    TimeLineID RecoveryTargetTLI;

 

    /* archiveCleanupCommand 是从 recovery.conf 文件里读的,但需要放在共享内存里以使 bgwriter 进程能访问它 */

    char        archiveCleanupCommand[MAXPGPATH];

 

    /* SharedRecoveryInProgress 指明本进程是否正在做崩溃或归档恢复。由

info_lck 锁保护 */

    bool       SharedRecoveryInProgress;

 

    /*

      * SharedHotStandbyActive 指明本进程是否正在做崩溃或归档恢复。由

info_lck 锁保护 */

    bool       SharedHotStandbyActive;

 

    /* 如果正在等 WAL 到达或者 failover 的触发器文件出现, recoveryWakeupLatch 用于唤醒启动将成继续重放 WAL */

    Latch      recoveryWakeupLatch;

 

    /* 在恢复期间,我们在这儿保存最后一个检查点的拷贝。当 bgwriter 想创建一个重启点 restartpoint 时由 bgwriter 进程使用。由 info_lck 锁保护。 */

    XLogRecPtr lastCheckPointRecPtr;

    CheckPoint lastCheckPoint;

 

    /* 最后一个检查点或被重放的检查点的结束位置加 1 */

    XLogRecPtr replayEndRecPtr;

    /* 被重放的最后一个记录的结束位置加 1 */

    XLogRecPtr recoveryLastRecPtr;

    /* 最后被重放的 COMMIT/ABORT 记录的时间戳 */

    TimestampTz recoveryLastXTime;

    /* 是否请求暂停恢复 ? */

    bool       recoveryPause;

 

    slock_t       info_lck;     /* locks shared variables shown above */

} XLogCtlData;

 

static XLogCtlData *XLogCtl = NULL;

 

/* XLogInsert 的共享状态数据结构 */

typedef struct XLogCtlInsert

{

    XLogwrtResult LogwrtResult; /* a recent value of LogwrtResult */

    XLogRecPtr PrevRecord;       /* start of previously-inserted record */

    int         curridx;      /* current block index in cache */

    XLogPageHeader currpage; /* points to header of block in cache */

    char       *currpos;      /* current insertion point in cache */

    XLogRecPtr RedoRecPtr;       /* current redo point for insertions */

    bool       forcePageWrites;  /* forcing full-page writes for PITR? */

 

    /* 如果在进程里备份由 pg_start_backup() 开始, exclusiveBackup true nonExclusiveBackups 是计数器,指明进程里当前基于流备份的数目。当上面两个任一个非 0 时(即有上面的备份时), forcePageWrites ture lastBackupStart 是最后一个检查点的 redo 值(下一个 xlog 记录的位置指针),作为在线备份的起始点。 */

    bool       exclusiveBackup;

    int         nonExclusiveBackups;

    XLogRecPtr lastBackupStart;

} XLogCtlInsert;

 

XLOG 控制的共享内存数据结构, LogwrtRqst 指出我们需要写 / 文件同步到日志的那个字节位置(在这个位置之前的所有记录必须被写或做文件同步)。 LogwrtResult 指出我们已经写 / 文件同步了的字节位置。

typedef struct XLogwrtRqst

{

    XLogRecPtr Write;        /* last byte + 1 to write out */

    XLogRecPtr Flush;        /* last byte + 1 to flush */

} XLogwrtRqst;

 

typedef struct XLogwrtResult

{

    XLogRecPtr Write;        /* last byte + 1 written out */

    XLogRecPtr Flush;        /* last byte + 1 flushed */

} XLogwrtResult;

 

指向 XLOG 里位置的指针。这个指针是 64 位,因为我们不想它有溢出的时候。

注意:用来指明一个无效的指针。这个没问题,因为我们在 XLOG 页头用了页头结构,因此 XLOG 记录不可能从页头开始。

注意:这儿容易引起理解错乱,这个 xlogid (对应实际 XLOG 文件名字的中间八位)表示逻辑 XLOG 日志文件 ID ,因为组成 XLOG 逻辑文件的实际物理文件远小于 4Gb 。组成对应这个 xlogid 的逻辑日志文件的每一个实际物理文件是一个 XLogSegSize 字节大小的“段”( "segment" ,段号是实际 XLOG 文件名字的后八位)。前面加上用八位表示的一个时间线 ID 、逻辑日志文件号和段号一起标识一个物理的 XLOG 日志文件(“段”)。段号和物理文件里的偏移量由 xrecoff/XLogSegSize xrecoff%XLogSegSize 计算。

typedef struct XLogRecPtr

{

    uint32      xlogid ;           /* log file #, 0 based */

    uint32      xrecoff ;      /* byte offset of location in log file */

} XLogRecPtr ;

 

/* XLogWrite/XLogFlush 的共享内存里的状态数据结构 */

typedef struct XLogCtlWrite

{

    XLogwrtResult LogwrtResult; /* current value of LogwrtResult */

    int         curridx;      /* cache index of next block to write */

    pg_time_t  lastSegSwitchTime;       /* time of last xlog segment switch */

} XLogCtlWrite;

 

/* 系统状态指示器。 */

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 ;

 

XLogStartup 流程示意图中的两个红色方框红色字的框是 XLOG 资源管理器 xmgr 的处理方法,这个 XLOG 的资源管理器内容较多,单列主题讨论。还有恢复完成后调用了方法 CreateCheckPoint ,创建一个检查点 以将所有的恢复数据写到磁盘

 

5 创建检查点

创建一个检查点,会将共享内存里的所有磁盘缓冲和提交日志缓冲刷出并文件同步到磁盘。

下面这些情况可能引起创建检查点,为了使用方便,把这些情况定义成如下标志,这些标志可以按位做或运算。检查点的起因不同,创建检查点的行为也略有不同。

#define CHECKPOINT_IS_SHUTDOWN   0x0001 /* Checkpoint is for shutdown */

#define CHECKPOINT_END_OF_RECOVERY 0x0002     /* Like shutdown checkpoint,

                                            * but issued at end of WAL

                                            * recovery */

#define CHECKPOINT_IMMEDIATE    0x0004 /* Do it without delays */

#define CHECKPOINT_FORCE    0x0008 /* Force even if no activity */

/* These are important to RequestCheckpoint */

#define CHECKPOINT_WAIT         0x0010 /* Wait for completion */

/* These indicate the cause of a checkpoint request */

#define CHECKPOINT_CAUSE_XLOG   0x0020 /* XLOG consumption */

#define CHECKPOINT_CAUSE_TIME   0x0040 /* Elapsed time */

 

创建检查点的基本过程是先让存储管理器 smgr (以后单列状态讨论)为检查点做好准备, 根据情况填充检查点结构的成员,CheckpointGuts方法把共享内存里的磁盘缓冲和提交日志缓冲输出到磁盘(即写数据文件)。接着调用XlogInsert把这个检查点插入xlog文件。然后更新控制文件相关成员。最后更新共享内存里XlogCtl的检查点相关成员和检查点的统计信息结构。相关结构定义和创建检查点流程示意图见下面。

 

/* 检查点统计信息 */

typedef struct CheckpointStatsData

{

    TimestampTz ckpt_start_t ;   /* start of checkpoint */

    TimestampTz ckpt_write_t ;   /* start of flushing buffers */

    TimestampTz ckpt_sync_t ; /* start of fsyncs */

    TimestampTz ckpt_sync_end_t ;    /* end of fsyncs */

    TimestampTz ckpt_end_t ;     /* end of checkpoint */

 

    int         ckpt_bufs_written ;       /* # of buffers written */

 

    int         ckpt_segs_added ;  /* # of new xlog segments created */

    int         ckpt_segs_removed ;       /* # of xlog segments deleted */

    int         ckpt_segs_recycled ;      /* # of xlog segments recycled */

 

    int         ckpt_sync_rels ; /* # of relations synced */

    uint64      ckpt_longest_sync ;       /* Longest sync for one relation */

    uint64      ckpt_agg_sync_time ;      /* The sum of all the individual sync

                                     * times, which is not necessarily the

                                     * same as the total elapsed time for

                                      * the entire sync phase. */

} CheckpointStatsData ;

 

当前检查点的统计信息收集在这个全局结构变量里。

CheckpointStatsData CheckpointStats ;

 

 

创建检查点流程示意图

 

上图中,其中 CheckPointGuts 方法的定义见下面,刷出所有共享内存中的数据到磁盘并做文件同步。方法定义见下面,把 clog subtrans multixact predicate relationmap buffer (数据文件)和 twophase 相关数据统统刷和文件同步到磁盘。这儿先不深入讨论这个方法了。

static void

CheckPointGuts(XLogRecPtr checkPointRedo, int flags)

{

    CheckPointCLOG();

    CheckPointSUBTRANS();

    CheckPointMultiXact();

    CheckPointPredicate();

    CheckPointRelationMap();

    CheckPointBuffers(flags);   /* performs all required fsyncs */

    /* We deliberately delay 2PC checkpointing as long as possible */

    CheckPointTwoPhase(checkPointRedo);

}

 

结果这么多逻辑严谨的一系列行为后,数据库达到了正常状态,启动进程寿终正寝。然后, postmaster 进程响应该子进程退出,分别依次 fork bgwriter 进程、 walwriter 进程、 autovaclauncher 进程、 archiver 进程、 pgstat 进程,然后抛出一句 database system is ready to accept connections 。然后进入 serverloop ,等待客户端请求到达,启动 postgres 服务进程,开始履行使命。

Serverloop 还检查 bgwriter 进程、 walwriter 进程、 autovaclauncher 进程、 archiver 进程、 pgstat 进程,还有前面启动的系统日志进程 sysloger 这些辅助检查是否正常运行,如果没有,就重启这些进程。此时, pg 服务器端有 postmaster 进程和这六个辅助进程运行,准备好为客户端进程提供服务,提供的服务由 postgres 服务进程完成。

 

 

------------
转载请著明出处,来自博客:
blog.csdn.net/beiigang
beigang.iteye.com

 

  • 大小: 62.5 KB
  • 大小: 109.9 KB
  • 大小: 82.4 KB
0
0
分享到:
评论

相关推荐

    PostgreSQL中文手册9.2

    一、服务器进程的启动和关闭: 一、服务器进程的启动和关闭: 一、服务器进程的启动和关闭: 一、服务器进程的启动和关闭: 一、服务器进程的启动和关闭: 一、服务器进程的启动和关闭: . 50 PostgreSQL PostgreSQL...

    Go-PostgreSQLBGWorker用Go编写的PostgreSQL后台工作进程

    在PostgreSQL中,BGWorker是用户定义的后台进程,它们不是作为数据库服务器的一部分启动,而是由服务器启动并在需要时执行特定的任务。这种设计使得BGWorker能够执行长时间运行的操作,而不阻塞主线程或影响其他...

    Linux下PostgreSQL安装与开机启动

    ### Linux下PostgreSQL安装与开机启动详解 #### 1. 添加用户及创建目录 为了确保PostgreSQL服务的安全性,我们通常会为它创建一个独立的系统用户。这一步骤包括了用户创建、密码设定以及相关目录的搭建。 ##### ...

    postgresql--内核分析--多进程结构

    - PostgreSQL采用了多进程模型,每个连接到数据库的客户端都会启动一个新的后端进程。 - 主要进程包括`postmaster`(负责监听客户端请求并启动其他进程)、`backend processes`(处理客户端查询)、`wal writer`...

    PostgreSQL博客1

    1. **postgres主进程**:这是启动数据库集群的父进程,负责管理其他子进程,如背景工作者和服务进程。 2. **后台工作者**:包括检查点进程、归档进程、统计收集器、Wal Writer等,它们执行各种后台任务,确保数据库...

    linux配置postgresql

    Linux 环境下配置 PostgreSQL 数据库是一个复杂的过程,需要多个步骤来完成。下面将详细介绍这些步骤,并解释每个步骤的重要性。 第一步:建立 postgres 用户 PostgreSQL 的数据库主进程 postmaster 是一个特殊的...

    postgresql12主从集群安装

    然后,在从节点上启动复制进程,并持续监控复制状态以确保数据的一致性。 在实际部署中,你可能还需要考虑其他高可用性和性能优化策略,比如使用负载均衡器、设置额外的从节点以及定期对主从节点进行健康检查。此外...

    数据库的启动与关闭

    数据库的启动与关闭

    postgresql 12、15离线安装包

    1. PostgreSQL的二进制文件:这是运行数据库服务的核心部分,包括postgres主进程和其他相关服务。 2. 数据库管理工具:如pgAdmin,用于图形化管理数据库,进行创建、查询、备份等操作。 3. 系统库和依赖:这些是...

    PostgreSQL_原理简介

    #### 二、进程结构 PostgreSQL 采用的是“每个用户一个进程”的模型。这是因为: - **为何选择进程而非线程**: - 线程库在不同操作系统上的支持情况存在差异,可能导致复杂度增加。 - 单个进程的故障不会影响...

    PostgreSQL 12.2安装与使用

    * 二进制安装包安装:下载 PostgreSQL 12.2 二进制安装包,然后按照安装包中的 README 文件进行安装。 * yum 安装:使用 yum search postgresql && yum install postgresql 命令安装 PostgreSQL 12.2。 * 源码编译...

    linux postgresql 安装步骤

    如果命令行显示了与PostgreSQL相关的进程,则表示PostgreSQL已安装在系统中。此时可以通过以下命令卸载: ```bash # 卸载所有PostgreSQL相关包 yum -y remove postgresql* # 如果有必要,可以进一步清理遗留的RPM...

    PostgreSQL内核分析

    1. **Postmaster**:这是PostgreSQL启动后创建的第一个进程,负责创建共享内存、初始化锁表和数据库缓冲区等内部数据结构,并监听用户请求,根据需要创建`postgres`进程来处理用户的服务请求。此进程在整个数据库...

    postgresqlAPI

    - 介绍了如何启动和关闭PostgreSQL服务器进程。 8. PostgreSQL的角色和权限: - 提供了关于数据库角色的管理和权限分配的知识。 9. PostgreSQL的数据库管理: - 概述了数据库管理的基本概念和操作。 10. ...

    PostgreSQL pg_ctl start启动超时实例分析

    1. 主机(非热备):当startup进程完成启动过程并退出时,它会向主进程发送SIGCHLD信号,主进程收到信号后会在`postmaster.pid`文件中写入"ready"。 2. 热备机(hot standby): - 如果在数据恢复前未达到一致性...

    PostgreSQL教程(十一):服务器配置

    在PostgreSQL中,服务器配置是管理和优化数据库性能的关键环节。本教程主要涵盖服务器进程的启动和关闭以及配置参数的设置。 ### 一、服务器进程的启动和关闭 PostgreSQL服务器的管理通常通过`pg_ctl`命令进行,它...

    postgresql-13.0.tar.gz

    这个“postgresql-13.0.tar.gz”文件是PostgreSQL的13.0版本的源代码压缩包,适用于那些希望在自己的系统上编译和安装数据库服务器的用户。PostgreSQL 13.0版本引入了多项增强和改进,旨在提升性能、可扩展性和用户...

    postgresql-9.2.4-1安装包

    - 如果在安装过程中没有启动服务,可以在服务管理器中找到 "postgresql-x64-9.2" 或 "postgresql-9.2" 服务并手动启动。 - 使用命令行工具 `pg_ctl` 或图形化工具如 pgAdmin 来管理数据库服务,包括启动、停止和...

    MySQL和PostgreSQL的比较

    相比之下,PostgreSQL的实例启动依赖于`Postmaster`进程,通常通过`pg_ctl`命令执行。一个实例同样可以管理多个数据库,但这些数据库被组织成一个集群,存储在一个初始化时设定的磁盘区域中,该区域由一个目录构成,...

Global site tag (gtag.js) - Google Analytics