PostgreSQL启动过程中的那些事十八:bgwriter进程一

本文探讨了PostgreSQL中后台写进程(bgwriter)的启动流程及其主要职责,包括释放共享缓存、处理检查点及脏页写入。

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

1

StartupDataBase调用流程略图

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

这一节讨论后台写进程bgwriter的启动过程,其是在Postgres8.0里新加的。

需要释放共享缓存以读入其他页面时,后台写进程(bgwriter)写出脏共享缓存数据(shared buffer)。在最好情况下所有从共享缓存里往外写将由后台写进程(bgwriter)发起。但是,如果后台写进程(bgwriter)没能维护足够多的干净共享缓存,其它后台进程仍然有权发起写脏共享缓存数据。

后台写进程(bgwriter)还负责处理所有检查点(checkpoint)。在从上次以来定量时间之后他将自动分派检查点,且后台写进程(bgwriter)也能够被发信号执行被请求的检查点。(每当很多WAL段被使用,后台进程填满WAL段时,通过后台进程信号通知后台写进程(bgwriter), GUC参数安排/委任一个检查点;后台写进程(bgwriter)自己不查看条件。)

后台写进程(bgwriter)由postmaster进程在启动子进程(startup process)一结束就启动,或者如果我们正在做归档回复,恢复进程一开始就启动。他一起存活到postmaster命令他结束。通常结束后台写进程(bgwriter)是通过SIGUSR2信号,且指示后台写进程(bgwriter)执行一个关闭检查点(shutdown checkpoint)并退出(exit(0))。(在SIGUSR2信号发起之前所有backend进程必须被停止!)紧急结束通过信号SIGQUIT;和任何后台进程(backend)一样,后台写进程(bgwriter)将简单退出且在SIGQUIT信号上退出。

如果后台写进程(bgwriter)非预期退出,postmaster进程象处理backend进程崩溃一样处理:共享内存可能被破坏,因此现有的backend进程应该通过SIGQUIT信号被杀掉而且后面启动一个恢复周期。(甚至如果共享内存没有被破坏,pg丢失了下一个检查点哪些文件需要同步的信息,因此需要强制做一个系统重启。)

pg有三个GUC参数:bgwriter_delay(默认200毫秒)、bgwriter_lru_maxpages(默认100)、bgwriter_lru_multiplier(默认2.0)。系统每个bgwriter_delay时间唤醒bgwriter,扫描LRU链表,最多写出N×bgwriter_lru_multiplier且不超过bgwriter_lru_maxpages个脏缓冲页,N是最近两次运行bgwriter期间系统申请的缓冲页数。如果Bgwriter频繁写经常更新的页会增加I/O次数而性能下降,但如果bgwriter间隔很长时间才写,又不能优化I/O,这个需要根据实际情况权衡。

2

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


bgwriter进程的调用流程略图

在启动进程里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)BackgroundWriterMain方法是辅助进程bgWriter进程的入口函数,其主要工作都在这个方法里。

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 */

PGSemaphoreData sem; /* 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 prepared xact */

/* 当后台进程仍在启动时这些字段是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 */

struct PGPROC*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 XidCache subxids; /* cache for subtransaction XIDs */

};

typedefstruct SHM_QUEUE

{

struct SHM_QUEUE *prev;

struct SHM_QUEUE *next;

} SHM_QUEUE;

第10步调用BackgroundWriterMain方法,做了bgwriter进程的主要工作。


AuxiliaryProcessMain的流程图

在AuxiliaryProcessMain方法中,设置本进程运行模式为引导模式,调用BaseInit方法初始化了一个虚拟文件描述符结构Vfd的头指针VfdCache并注册了进程退出是清理临时文件的函数,接着初始化了存储管理器,这个另行讨论,最后初始化了本地记录每个缓存信息的数组。然后根据情况ProcSignalInit为辅助进程分配ProcSignalSlot,调用InitBufferPoolBackbend方法,在其中调用on_shmem_exit注册共享内存退出清理缓存相关资源要调用的函数AtProcExit_Buffers。然后把进程设回通常模式。根据传入参数调用BackgroundWriterMain,做了bgwriter进程的主要主要工作。

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


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



<think>好的,我现在需要帮助用户理解PostgreSQLBgWriter后台写进程的工作原理和优化方法。首先,我应该回顾用户提供的引用资料,确保信息准确。用户引用了多个来源,包括进程结构、BgWriter的监控命令、源码分析、原理介绍以及参数配置。 首先,我需要明确BgWriter的基本职责。根据引用[3],BgWriter主要负责将共享缓冲区中的脏页刷盘,目的是提升性能,避免在没有BgWriter时可能出现的性能问题。所以工作原理部分需要解释BgWriter如何管理脏页的写入,以及它如何检查点协作。 接下来是工作原理的详细部分。引用[5]提到bgwriter_flush_after参数,这涉及OS层面的写入策略,说明BgWriter不仅写脏页,还控制写入的频率和量,以避免IO瓶颈。另外,引用[4]可能提供了BgWriter的工作流程,比如LRU算法或环形扫描策略,但具体细节需要确认。用户的问题还涉及优化方法,所以需要整理相关参数和监控手段。 关于优化,引用[2]给出了监控BgWriter的命令,比如检查进程状态和内存使用。同时,引用[5]提到的参数如bgwriter_delay、bgwriter_lru_maxpages等,调整这些参数可以优化BgWriter的行为。需要将这些参数的作用和调整建议结合起来,比如如何平衡后台写入和检查点带来的IO压力。 此外,用户可能想知道如何根据工作负载调整这些参数。比如,高写入负载下可能需要增加lru_maxpages,但也要注意避免占用过多IO资源。同时,监控pg_stat_bgwriter视图中的统计信息是关键,如buffers_clean和maxwritten_clean,这些数据能帮助评估BgWriter的效果。 需要注意的是,用户可能对某些术语不太熟悉,比如共享缓冲区、脏页、检查点等,所以在解释时应保持清晰,但根据用户的问题,可能已经有定的基础,因此可以适当深入。 最后,确保回答结构清晰,分点说明工作原理和优化方法,引用相关的引用资料来支持每个点,比如在解释BgWriter参数时引用[5],在讨论监控时引用[2]和[4]。 总结步骤: 1. 介绍BgWriter的作用和存在意义。 2. 分点详细说明工作原理,包括写入策略、检查点的协作、参数影响。 3. 优化方法部分,列出关键参数及其调整建议,监控手段。 4. 结合引用资料中的命令和参数说明,确保信息准确。 5. 最后生成相关问题,帮助用户进步探索。 需要检查是否有遗漏的引用信息,比如引用[1]提到Postmaster进程,可能BgWriter启动有关,但用户的问题更关注BgWriter本身,所以可能不需要深入这部分。确保所有引用的资料都被正确引用,并在适当的位置添加引用标识。</think>PostgreSQLBgWriter(后台写进程)是数据库性能优化的核心组件,其工作原理和优化方法如下: ### BgWriter工作原理 1. **脏页管理机制** BgWriter持续扫描共享缓冲区的$LRU$链表,将近期最少使用的脏页(被修改但未写入磁盘的数据页)分批写入磁盘,减少检查点(Checkpoint)时的集中IO压力[^3]。通过环形扫描策略,避免重复写入同区域。 2. **写入策略优化** - 使用参数$bgwriter\_lru\_maxpages$控制单次最大写入页数(默认100页),通过$bgwriter\_delay$设置轮询间隔(默认200ms)[^5]。 - 采用"惰性写入"模式:仅在系统空闲或缓冲区不足时触发,优先清理可复用页面,减少对务性能的影响[^4]。 3. **检查点协作** 检查点要求将所有脏页强制刷盘,而BgWriter通过持续写入部分脏页,显著降低检查点期间的IO尖峰。两者配合公式: $$ \text{总IO压力} = \text{BgWriter写入量} + \text{检查点写入量} $$ ### 二、优化方法 1. **关键参数调优** | 参数 | 作用 | 建议值 | |---|---|---| | `bgwriter_lru_multiplier` | 基于最近使用频率的动态写入量计算系数 | 2.0(默认)可增至3-4 | | `bgwriter_flush_after` | 单次写入后强制刷盘的数据量(避免OS缓存堆积) | 512kB-1MB [^5] | | `checkpoint_timeout` | 延长检查点间隔(需配合BgWriter调优) | 15-30min | 2. **监控指标分析** ```sql SELECT * FROM pg_stat_bgwriter; ``` - `buffers_clean`:BgWriter主动清理的页面数 - `maxwritten_clean`:因达到$bgwriter\_lru\_maxpages$限制而终止扫描的次数 理想状态:`buffers_clean`检查点写入量的比例应大于3:1[^4]。 3. **操作系统级优化** ```bash # 监控BgWriter内存使用 watch -n 1 'ps -p $(pgrep -f "bgwriter") -o %mem,rss' # [^2] ``` 结合`vmstat`观察IO等待时间,确保BgWriter的IOPS不超过磁盘能力的70%。 ### 三、典型场景优化 1. **高写入负载** 增加`bgwriter_lru_maxpages`(如500)并缩短`bgwriter_delay`(如100ms),同时监控`maxwritten_clean`是否频繁触发。 2. **SSD存储环境** 降低`bgwriter_flush_after`(如128kB),利用SSD高随机写入性能特性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值