PostgreSQL启动过程中的那些事十九:walwriter进程一

来源:互联网 发布:程序员 交易员 编辑:程序博客网 时间:2024/04/27 14:49

        话说main()->PostmasterMain()->StartupDataBase(),fork了启动进程调用,调用StartupXLOG方法,启动XLOG、验证数据库一致性、根据情况做数据库恢复和创建检查点,然后启动进程退出。Postmaster进程响应启动进程退出信号,启动了后台写进程、WAL日志写进程、AUTOVACUUM进程、归档进程、统计进程这些辅助进程。

1

StartupDataBase调用流程略图

        这几个进程都调用自己相应的函数,组织不同入参,然后调用postmaster_forkexec函数,创建一个进程,根据不同的入参在SubPostmasterMain函数里走了不同的分支。

       这一节讨论walwriter的启动过程。该进程是pg 8.3里新加的。

walwriter后台进程尝试保持一个常规后台进程完成写出WAL页面和文件同步WAL页面。它还保证 未被伴随提交立即同步到磁盘的事务提交记录(即“异步提交”)在可知的时间里到达磁盘——象实际发生的那样,最多三次wal写延迟(wal_writer_delay)周期时间。

注意,象bgwriter进程对于共享缓存那样,当walwriter进程不能保持时,其它后台进程有权发起WAL写和文件同步。

Walwriter进程有postmaster进程中startup子进程一结束就启动。一直保留到postmaster进程命令其结束。通常结束时通过SIGTERM信号,其指示walwriter进程以exit(0)退出。紧急结束通过SIGQUIT信号;象其他任何后台进程,walwriter在SIGQUIT上快速退出。

如果walwriter非期望退出,postmaster进程等同后台进程崩溃处理:共享内存可能被干掉,因此保留的后台进程应该由SIGQUIT信号杀死然后开始一个恢复周期。

       WriteAhead XLog是在写事务数据到数据文件前先写事务日志XLOG。这样事务提交时可以线性文件同步事务日志到XLOG文件,异步写事务数据到数据文件,减少了集中I/O操作。

 

2

       walwriter进程启动后的调用流程图如下:

 

walwriter进程的调用流程略图


在启动进程里Main()->SubPostmasterMain(),调用了如下方法,启动XLOG后就结束了生命。

1)MemoryContextInt方法,参见《PostgresQL启动过程中的那些事一》;

2)InitializeGUCOptions方法,参见《PostgresQL启动过程中的那些事三》;

3)Read_backend_variablases方法,为重组BackendParameters结构读取前面存储的文件pgsql_tmp/pgsql_tmp.backend_var.[pid].[tmpFileNum];

4)PGSharedMemoryReAttach方法,attach进程postmaster里的共享内存;

5)read_nondefault_variables方法,读非默认GUC参数,参见《PostgresQL中的那些事十一:保存非默认GUC参数到文件》;

6)ClosePostmasterPorts方法,关闭“启动进程”不用的文件句柄,当然,在postmaster进程里这些文件还是打开的;

7)InitShmemAccess方法,在初始化本进程共享内存全局变量:这些shmem头的ShmemSegHdr、shmem起始地址ShmemBase和shmem结束地址+1的ShmemBase。定义见下面。

staticPGShmemHeader *ShmemSegHdr;          /* shared mem segment header */

staticvoid *ShmemBase;                     /* start address of shared memory */

staticvoid *ShmemEnd;                      /* end+1 address of shared memory */

8)InitAuxiliaryProcess方法,初始化一个PGPROC结构;

9)CreateSharedMemoryAndSemaphores方法,参见《PostgresQL中的那些事七》;

10)InitXlogAccess方法,根据在关系内存里的xlog控制结构XLogCtl初始化了全局变量ThisTimeLineID和RedoRecPtr。

11)WalWriterMain方法是辅助进程WalWriter进程的入口函数,其主要工作都在这个方法里。

 

        SubPostmasterMain的流程图见下面。根据启动进程的传入参数“postgres –forkboot NULL [v_AuxProcType]”走了"--forkboot"这个分支。还有bgwriter进程、WalWriter进程、WalReciver进程都走了这个分支,以后要讨论到相关进程,就直接从这个分支里开始了。还有AutoVacuumLauncher进程、AutoVacuumWorker进程、归档进程、统计进程以及为前端提供服务的postgres进程等在进程初始阶段,几乎没有区别的都走了1-6步,然后根据不同入参走了不同的分支,因此以后要讨论到这些进程,就直接从这些分支开始。

 


SubPostmasterMain的流程图

 

        第8步InitAuxiliaryProcess初始化了一个每个辅助进程都有一个的PGPROC结构。每一个后台进程都在共享内存里有一个PGPROC结构,共享内存里有一个未使用的PGPROC结构链表,从其中给新的后台进程分配。参见《PostgresQL启动过程中的那些事七初始化ProcGlobal》。

        当等待锁时,这个PGPROC结构被链入锁的等待进程队列。回收后的PGPROC链入ProcGlobal的空闲进程列表。

        注意,两阶段提交会为每一个当前已准备事务设置一个假的PGPROC。这些PGPROC出现在ProcArray数据结构里以使已准备事务显示其还在运行并且能正确显示其持有锁。已准备事务的PGPROC和真实进程的PGPROC的区别是已准备事务的PGPROC的pid等于0。在已准备事务的PGPROC里不使用信号和锁行为,但是它的myProcLocks[]列表是有效的。

struct PGPROC

{

    /*proc->links必须是结构的第一个成员*/

    SHM_QUEUE links;       /* list link if process is in a list */

 

    PGSemaphoreDatasem;    /* ONE semaphore to sleep on */

    int       waitStatus;      /*STATUS_WAITING, STATUS_OK or STATUS_ERROR */

 

    LocalTransactionIdlxid;/* local id of top-level transaction currently

                            *being executed by this proc, if running;

                            *else InvalidLocalTransactionId */

 

    TransactionIdxid;         /* id of top-level transaction currently being

                            *executed by this proc, if running and XID

                            *is assigned; else InvalidTransactionId */

 

    TransactionIdxmin;        /* minimal running XID as it was when we were

                            *starting our xact, excluding LAZY VACUUM:

                            *vacuum must not remove tuples deleted by

                            * xid>= xmin ! */

 

    int       pid;         /* Backend's process ID; 0 if preparedxact */

 

    /*当后台进程仍在启动时这些字段是0: */

    BackendId backendId;   /* This backend's backend ID (if assigned) */

    Oid       databaseId;      /* OID of database this backendis using */

    Oid       roleId;          /* OID of role using this backend */

 

    bool      inCommit;    /* true if within commit critical section */

 

    uint8     vacuumFlags/* vacuum-related flags, see above */

 

    /*当是热备模式时,显示已为当前事务发出冲突信号。尽管没有要求,当持有ProcArrayLock锁事设置/消除。如果需要,没有锁可以访问。

    bool      recoveryConflictPending;

 

    /*如果有,是进程当前正在等待的轻量锁的信息 */

    bool      lwWaiting;   /* true if waiting for an LW lock */

    bool      lwExclusive/* true if waiting for exclusive access */

    structPGPROC*lwWaitLink/* next waiter for same LW lock */

 

    /*如果有,进程当前正在等待的锁的信息*/

    /*如果当前没有等待的锁,waitLockwaitProcLockNULL */

    LOCK    *waitLock;    /* Lock object we're sleeping on ... */

    PROCLOCK  *waitProcLock;/* Per-holder info for awaited lock */

    LOCKMODE  waitLockMode;/* type of lock we're waiting for */

    LOCKMASK  heldLocks;   /* bitmask for lock types already held on this

                            *lock object by this backend */

 

    Latch     procLatch;   /* generic latch for process */

 

    /*如果需要,是允许本进程等待同步复制的信息。如果没有等待的话waitLSN的值是InvalidXLogRecPtr;仅由用户后台进程设置。除非属主进程或WALSender进程可以touch syncRepState。仅当持有SyncRepLock锁时才可以用syncRepLinks

     */

    XLogRecPtrwaitLSN;     /* waiting for this LSN or higher */

    int       syncRepState;/* wait state forsync rep */

    SHM_QUEUE syncRepLinks;/* list link if process is in syncrepqueue */

 

    /*为锁持有的所有PROCLOCK对象或者由该后台进程等待的PROCLOCK被链入这些链表中的一个,根据他们锁的分区号。

    所有为持有锁或者有后台进程等待的PROCLOCK对象被链到这个列表,股他们锁的发布号。

     */

    SHM_QUEUE myProcLocks[NUM_LOCK_PARTITIONS];

 

    struct XidCachesubxids;/* cache for subtransaction XIDs */

};

 

typedefstruct SHM_QUEUE

{

    struct SHM_QUEUE *prev;

    struct SHM_QUEUE *next;

} SHM_QUEUE;

       第10步调用InitXlogAccess方法,根据在关系内存里的xlog控制结构XLogCtl初始化了全局变量ThisTimeLineID和RedoRecPtr。

       第11步调用WalWriterMain方法,做了walwriter进程的主要工作。


 

 AuxiliaryProcessMain的流程图

        在AuxiliaryProcessMain方法中,设置本进程运行模式为引导模式,调用BaseInit方法初始化了一个虚拟文件描述符结构Vfd的头指针VfdCache并注册了进程退出是清理临时文件的函数,接着初始化了存储管理器,这个另行讨论,最后初始化了本地记录每个缓存信息的数组。然后根据情况ProcSignalInit为辅助进程分配ProcSignalSlot,调用InitBufferPoolBackbend方法,在其中调用on_shmem_exit注册共享内存退出清理缓存相关资源要调用的函数AtProcExit_Buffers。然后把进程设回通常模式。根据传入参数先调用了InitXLOGAccess方法,先从共享内存的XLogCtl结构里取了当前时间线ID ThisTimeLineID放到同名的变量里,然后又从XLogCtl结构的XLogCtlInsert类型成员Insert里取了最后检查点的下一个记录位置RedoRecPtr放到同名变量里。接着调用WalWriterMain,做了walwriter进程的主要主要工作。

        先到这儿吧,下一篇接着讨论WalWriterMain方法。


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