暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【干货分享】进程与内存架构

云贝教育 2021-11-12
497



点击上方蓝色文字关注我吧





目录


1、进程架构的概述

2、内存架构的概述

3、核心进程简介

4、核心进程源码解析


 



进程架构概述 –体系架构概览




记忆方法:3 + 5 + 8 + 5

 


postgres  118495      1    /opt/pgsql10.12/bin/postgres
postgres 18674 118495 postgres: logger process
postgres 18686 118495 postgres: checkpointerprocess
postgres 18687 118495 postgres: writer process
postgres 18688 118495 postgres: wal writerprocess
postgres 18689 118495 postgres: autovacuum launcherprocess
postgres 18690 118495 postgres: stats collectorprocess
postgres 18691 118495 postgres: bgworker: logicalreplication launcher
postgres 46841 118495 postgres: postgres postgres[local] idle


复制

 


 

进程

说明

服务器进程

所有后台/后端进程的父进程

后端进程

处理客户端发出的语句请求

后台进程

负责数据库的管理任务

复制相关进程

流复制相关

 





内存架构概述




PostgreSQL 中的内存架构可以分为两大类:
本地内存区域—由每个后端进程分配供自己使用。
共享内存区域—由 PostgreSQL服务器的所有进程使用。



 

组件

描述

work_mem

 ORDER BY 和 DISTINCT 操作对元组进行排序

maintenance_work_mem    

维护操作(VACUUM、REINDEX、alter等)

temp_buffers

使用这个区域来存储临时表。

shared buffer pool

数据缓冲区

WAL buffer

WAL记录缓冲区

commit log   

事务提交日志缓冲区

 




进程、参数概览










postgres(postmaster)概述‍




postgres 服务器进程是PostgreSQL 服务器中所有进程的父进程。在早期版本中,它被称为”postmaster”。

通过pg_ctlstart,postgres 服务器进程将启动。它在内存中分配一个共享内存区域,启动各种后台进程,必要时启动复制

关联进程和后台工作进程,并等待来自客户端的连接请求。

每当收到来自客户端的连接请求时,它就会启动一个后端进程。(后端进程处理连接的客户端发出的所有请求)

一个postgres服务器进程监听一个网络端口,默认端口是5432。虽然同一台主机上可以运行多个PostgreSQL服务(不同集

簇),但是每个服务器之间应该设置监听不同的端口号。

 



核心进程源码浅析



 

postgres(postmaster)源码浅析 1

src/bin/pg_ctl/pg_ctl.c
复制

 


postgres(postmaster)源码浅析 2

src/backend/postmaster/postmaster.c
复制
ListenAddresses
/* The TCP listen address(es) */
char *ListenAddresses;
if (ListenAddresses) (IP地址才能判断为真)
char *rawstring;
List *elemlist;
ListCell *l;
int success = 0;


复制
bacend process
/*
* BackendStartup -- start backend process
*
* returns: STATUS_ERROR if the fork failed,STATUS_OK otherwise.
*
* Note: if you change this code, also considerStartAutovacuumWorker.
*/
static int
BackendStartup(Port*port)
复制



BackendProcesses 概述

后端进程,也称为postgres,由 postgres 服务器进程启动并处理由一个连接的客户端发出的所有语句。

它通过单个 TCP 连接与客户端通信,并在客户端断开连接时终止。

由于只允许操作一个数据库,因此在连接到 PostgreSQL 服务器时,必须明确指定要使用的数据库。

允许多个客户端同时连接;配置参数max_connections控制客户端的最大数量(默认为 100)。

如果WEB应用等很多客户端频繁重复连接和断开与PostgreSQL服务器的连接,会增加建立连接和创建后端进

程的成本,因为PostgreSQL没有实现本地连接池功能。这种情况对数据库服务器的性能有负面影响。为了处理这种情况,通常使用池中间件(pgbouncer 或pgpool-II)。

 

 

Backend Processes 源码浅析

src/backend/tcop/postgres.c
复制
为用户交互连接调用InteractiveBackend()
static int
InteractiveBackend(StringInfo inBuf)
{
intc; /* character read from getc() */
/*
* display a prompt and obtain input from theuser
*/
printf("backend>");
复制

 

 



BackendProcesses 相关参数浅析

max_connections 活跃的并发连接数,最大连接数。

superuser_reserved_connections 为管理员保留的连接数。

tcp_keepalives_idle
复制

规定在操作系统向客户端发送一个TCP keepalive消息后无网络活动的时间总量。如果指定值时没有单位,则以秒为单位。值0(默认值)表示选择操作系统默认值。指定不活动多少秒之后通过 TCP 向客户端发送一个 keepalive 消息。0 值表示使用默认值。这个参数只有在支持TCP_KEEPIDLE或等效套接字选项的系统或Windows 上才可以使用。在其他系统上,它必须为零。在通过 Unix 域套接字连接的会话中,这个参数被忽略并且总是读作零。

tcp_keepalives_interval
复制

规定未被客户端确认收到的TCP keepalive消息应重新传输的时间长度。如果指定值时没有单位,

则以秒为单位。值0(默认值)表示选择操作系统默认值。这个参数只有在支持TCP_KEEPINTVL或等效套接字选项的系统或Windows 上才可以使用。

在其他系统上,必须为零。在通过 Unix域套接字连接的会话中,这个参数被忽略并总被读作零。

tcp_keepalives_count
复制

指定服务器到客户端的连接被认为中断之前可以丢失的TCP keepalive消息的数量。值0(默认值)表示选择操作系统默认值。这个参数只有在支持TCP_KEEPCNT或等效套接字选项的系统上才可以使用。在其他系统上,必须为零。在通过 Unix 域套接字连接的会话中,这个参数被忽略并总被读作零。

tcp_user_timeout
复制

指定传输的数据在TCP连接被强制关闭之前可以保持未确认状态的时间量。如果指定值时没有单位,则以毫秒为单位。值0(默认值)表示选择操作系统默认值。这个参数只有在支持TCP_USER_TIMEOUT的系统上才被支持;在其他系统上,它必须为零。在通过Unix-domain套接字连接的会话中,此参数将被忽略并且始终读取为零。


 

checkpointerprocess 概述

检查点进程,有点儿类似于虚拟机的快照。检查的作用主要是缩短数据库实例恢复的时间。

本图摘自” The Internals of PostgreSQL fordatabase administrators and system developers”

 


checkpointerprocess 源码浅析

src/backend/postmaster/checkpointer.c




src/include/catalog/pg_control.h
复制

 CheckpointerMain函数是checkpointer进程的入口.

检查点结构体定义

/*

 *检查点XLOG记录的主体。这是在这里声明的,因为我们在这里保存了最新版本的副本pg_control可能的灾难恢复。更改此结构需要PG_CONTROL_VERSION。


 */
typedef structCheckPoint
{
XLogRecPtr redo;/* 创建存盘时下一个可用的RecPtr(即重做点)*/
TimeLineID ThisTimeLineID; /*当前时间线 ID TLI */
TimeLineID PrevTimeLineID; /*当前一个时间线*/
boolfullPageWrites; /* 当前整页写入的状态*/
uint32 nextXidEpoch; /* 下一个事物ID的高位*/
TransactionId nextXid; /*下一个空闲事物ID */
Oid nextOid; /* 下一个空闲Oid*/
MultiXactId nextMulti; /*下一个空闲 MultiXactId */
MultiXactOffset nextMultiOffset;/* 下一个空闲 MultiXact 偏移量 */
TransactionId oldestXid; /* 集簇范围最小的 datfrozenxid */
Oid oldestXidDB; /* 带有最小datfrozenxid的数据库 */
MultiXactId oldestMulti; /* 集簇范围内最小的datfrozenxid */
Oid oldestMultiDB; /* 带有最小datminmxid的数据库 */
pg_time_t time;/* 存盘时间栈*/
TransactionId oldestCommitTsXid; /* oldest Xid with valid commit *timestamp */
TransactionId newestCommitTsXid; /* newest Xid with valid commit * timestamp*/
/*
*最老的XID仍在运行。这只需要从在线检查点初始化热备用模式,所以我们只需要为在线检查点计算这一点,并且仅当wal_级别为副本时。否则它将设置为InvalidTransactionId。
*/
TransactionId oldestActiveXid;
} CheckPoint;
复制


 

 

checkpointerprocess 相关参数浅析

checkpoint_timeout = 5min              # range 30s-1d
服务器的检查点进程常常自动地执行一个检查点。检查点在每checkpoint_timeout秒开始,或者在快要超过 max_wal_size时开始。默认的设置分别是 5 分钟和 1 GB。如果从前一个检查点以来没有WAL被写入, 则即使过了checkpoint_timeout新的检查点也会被跳过(如果正在使用WAL归档并且你想对文件被归档频率设置一个较低的限制来约束 潜在的数据丢失,你应该调整archive_timeout 参数而不是检查点参数)。也可以使用SQL命令 CHECKPOINT来强制一个检查点。
max_wal_size = 1GB
min_wal_size = 80MB
checkpoint_completion_target = 0.5     # checkpoint target duration, 0.0 - 1.0
为了避免大批页面写入对I/O系统产生的冲击,一个检查点中对脏缓冲区的写出操作被散布到一段时间上。这个时间段由checkpoint_completion_target控制,它用检查点间隔的一个分数表示。I/O率将被调整,以便能按照要求完成检查点:当checkpoint_timeout给定的秒数已经过去,或者max_wal_size被超过之前会发生检查点,以先达到的为准。默认值为0.5,PostgreSQL被期望能够在下一个检查点启动之前的大约一半时间内完成每个检查点。在一个接近于正常操作期间最大I/O的系统上,你可能希望增加checkpoint_completion_target来降低检查点的I/O负载。但这种做法的缺点是被延长的检查点将会影响恢复时间,.
因为需要保留更多WAL段来用于可能的恢复操作。尽管checkpoint_completion_target可以被设置为高于1.0,但最好还是让它小于1.0(也许最多0.9),因为检查点还包含除了写出脏缓冲区之外的其他一些动作。1.0的设置极有可能导致检查点不能按时被完成,这可能由于所需的WAL段数量意外变化导致性能损失。
checkpoint_flush_after = 256kB         # measured in pages, 0 disables
在 Linux 和 POSIX 平台上,checkpoint_flush_after允许强制 OS 超过一个可配置的字节数后将检查点写入的页面刷入磁盘。否则,这些页面可能会被保留在OS 的页面缓存中,当检查点结束发出fsync时就会导致大量刷写形成延迟。这个设置通常有助于减小事务延迟,但是它也可能对性能带来负面影响,尤其是对于超过shared_buffers但小于 OS 页面缓存的负载来说更是如此。
checkpoint_warning = 30s               # 0 disables
检查点的代价相对比较昂贵,首先是因为它们要求写出所有当前为脏的缓冲区,正如以上讨论的,第二个原因是它们会导致额外的WAL流量。
因此比较明智的做法是将检查点参数设置得足够高,这样检查点就不会过于频繁地发生。你可以设置checkpoint_warning参数作为对于你的检查点参数的一种简单完整性检查。如果检查点的发生时间间隔比checkpoint_warning秒还要接近,一个消息将会被发送到服务器日志来推荐你增加max_wal_size。
偶尔出现的这样的消息并不会导致警报,但是如果它出现得太频繁,那么就应该增加检查点控制参数。如果你没有把max_wal_size设置得足够高, 那么在进行如大型COPY传输等批量操作的时候可能会导致出现大量类似的警告消息。

 

writer process 概述

后台将脏页面写出到磁盘的辅助进程,引入该进程主要为达到如下两个目的:
1.首先,数据库在进行查询处理时若发现要读取的数据不在缓冲区中时要先从磁盘中读入要读取的数据所在的页面,
此时如果缓冲区已满,则需要先选择部分缓冲区中的页面替换出去,如果被替换的页面没有被修改过,那么可以直
接丢弃,但如果要被替换的页已被修改,则必须先将这页面写出到磁盘中后才能替换,这样数据库的查询就有可能被
阻塞,通过使用BgWriter定期写出缓冲区中的部分脏页面到磁盘中,为缓冲区腾出空间,就可以降低查询被阻塞的可能
性。
2.做检查点时.需要把所有脏页写出到磁盘,通过BgWriter预先写出一些脏页,可以减少设置检查点,时要进行的IO操作。
BgWriter是8.0以后新加的特性。

 

BgWriterprocess 源码浅析

src/backend/postmaster/bgwriter.c
复制
BackgroundWriterMain方法是辅助进程bgWriter进程的入口函数,其主要工作都在这个方法里。
/*
 bgwriter进程的主要入口点这是从AuxiliaryProcessMain调用的,它已经创建了基本的执行环境。
 */
void
BackgroundWriterMain(void)
{
sigjmp_buf local_sigjmp_buf;
MemoryContextbgwriter_context;
bool prev_hibernate;
WritebackContextwb_context;
/*
* Properly accept or ignore signals thepostmaster might send us.
*
* bgwriter doesn't participate in ProcSignalsignalling, but a SIGUSR1
* handler is still needed for latch wakeups.
*/
…………………
}
复制

 


BgWriterprocess 相关参数浅析

bgwriter_delay = 200ms                 # 10-10000ms between rounds
复制
指定后台写入器活动轮次之间的延迟。在每个轮次中,写入器都会为一定数量的脏缓冲区发出写操作(可以用下面的参数控制)。然后它就休眠 bgwriter_delay的时长, 然后重复动作。当缓冲池中没有脏缓冲区时,不管 bgwriter_delay,它都会进入更长的休眠。如果指定值时没有单位,则以毫秒为单位。默认值是 200 毫秒(200ms)。注意在许多系统上,休眠延迟的有效解析度是 10 毫秒;因此,为bgwriter_delay设置一个 不是 10 的倍数的值与把它设置为下一个更高的 10 的倍数是一样的效果。这个选项只能在服务器命令行上或者在postgresql.conf文件中设置。
bgwriter_lru_maxpages = 100            # 0-1000 max buffers written/round
复制
在每个轮次中,不超过这么多个缓冲区将被后台写入器写出。把这个参数设置为零可禁用后台写出(注意被一个独立、专用辅助进程管理的检查点不受影响)。默认值是100 个缓冲区。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。
bgwriter_lru_multiplier = 2.0          # 0-10.0 multiplier on buffersscanned/round
复制
每一轮次要写的脏缓冲区的数目基于最近几个轮次中服务器进程需要的新缓冲区的数目。最近所需的平均值乘以bgwriter_lru_multiplier可以估算下一轮次中将会需要的缓冲区数目。脏缓冲区将被写出直到有很多干净可重用的缓冲区(然而,每一轮次中写出的缓冲区数不超过bgwriter_lru_maxpages)。因此,设置为 1.0 表示一种“刚刚好的”策略,这种策略会写出正好符合预测值的数目的缓冲区。更大大的值可以为需求高峰提供某种缓冲,而更小的值则需要服务进程来处理一些写出操作。默认值是 2.0。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。
bgwriter_flush_after = 512kB           # measured in pages, 0 disables
复制
只要后台写入的数据超过这个数量,尝试强制 OS 把这些写发送到底层存储上。这样做将限制内核页缓存中脏数据的量,降低了在检查点末尾发出一个 fsync 时或者 OS 在后台大批量写回数据时卡住的可能性。那常常会导致大幅度压缩的事务延迟,但是也有一些情况(特别是负载超过shared_buffers但小于 OS 页面高速缓存)的性能会降低。这种设置可能会在某些平台上没有效果。如果指定值时没有单位,则以块为单位,即为BLCKSZ 字节,通常为8kB.合法的范围在0(禁用受控写回)和2MB之间。Linux上的默认值是512kB,其他平台上是0(如果BLCKSZ不是8kB,则默认值和最大值会按比例缩放至这个值)。这个参数只能在postgresql.conf文件中或者服务器命令行上设置。

 


wal writerprocess 概述

预写日志(WAL),的中心思想是对数据文件的修改必须是只发生在这些修改,已经记录到日志之后的,也就是先写日志后写数据, 如果遵循这个过程,那么就不需要在每次事物提交的时候都把数据块刷回到磁盘,因为在出现崩溃的情况下,可以用日志来恢复数据库。使用WAL主要的好处就是显著地减少了写磁盘的次数,因为在日志提交的时候只需要把日志文件刷新到磁盘,而不是事物修改所涉及的所有数据文件。
同步顺序IO、异步随机IO

 

wal writerprocess 源码浅析

WalWriterMain();src/backend/postmaster/walwriter.c
/*
WalWriter process,主入口函数是WalWriterMain(void)
*/
Void WalWriterMain(void)
{
sigjmp_buf local_sigjmp_buf;
MemoryContextwalwriter_context;
int left_till_hibernate;
bool hibernating;
/*
* Properly accept or ignore signals thepostmaster might send us
*
* We have no particular use for SIGINT at themoment, but seems
* reasonable to treat like SIGTERM.
*/
pqsignal(SIGHUP,WalSigHupHandler); /* set flag to read config file */
pqsignal(SIGINT,WalShutdownHandler); /* request shutdown*/
pqsignal(SIGTERM,WalShutdownHandler); /* requestshutdown */
pqsignal(SIGQUIT,wal_quickdie);/* hard crash time */
pqsignal(SIGALRM,SIG_IGN);
pqsignal(SIGPIPE,SIG_IGN);
pqsignal(SIGUSR1,walwriter_sigusr1_handler);
pqsignal(SIGUSR2,SIG_IGN); /* not used */
… …


复制


 

wal writerprocess 相关参数浅析 1

wal_level = replica              
wal_level决定多少信息写入到 WAL中。默认值是replica,它会写入足够的数据以支持WAL归档和复制,包括在后备服务器上运行只读查询。minimal会去掉除从崩溃或者立即关机中进行恢复所需的信息之外的所有记录。minimal,replica, or logical
在 9.6 之前的版本中,这个参数也允许值archive和hot_standby。现在仍然接受这些值,但是它们会被映射到replica。
fsync = on
PostgreSQL服务器将尝试确保更新被物理地写入到磁盘,做法是发出fsync()系统调用。虽然关闭fsync常常可以得到性能上的收益,但当发生断电或系统崩溃时可能造成不可恢复的数据损坏。
synchronous_commit = on
on、remote_apply、remote_write、local和off
如果synchronous_standby_names为非空,这个参数也控制事务提交是否将等待它们的 WAL 记录被复制到后备服务器上。
1.on时,直到来自于当前同步的后备服务器的回复指示它们已经收到了事务的提交记录并将其刷入了磁盘,主服务器上的事务才会提交。
2.remote_apply时,提交将会等待,直到来自当前的同步后备的回复指示它们已经收到了该事务的提交记录并且已经应用了该事务,这样该事务才变得对后备上的查询可见。
3. remote_write时,提交将等待,直到来自当前的同步后备的回复指示它们已经收到了该事务的提交记录并且已经把该记录写出到它们的操作系统,这种设置足以保证数据在后备服务器的PostgreSQL实例崩溃时得以保存,但是不能保证后备服务器遭受操作系统级别崩溃时数据能被保持,因为数据不一定必须要在后备机上达到稳定存储。 
4. local会导致提交等待本地刷写到磁盘而不是复制完成。在使用同步复制时这通常不是我们想要的效果,但是为了完整性,还是提供了这样一个选项。
如果synchronous_standby_names为空,设置on、remote_apply、remote_write和local都提供了同样的同步级别:事务提交只等待本地刷写磁盘。

 

wal writerprocess 相关参数浅析 2

wal_sync_method
open_datasync(用open()选项O_DSYNC写WAL 文件)。
O_DSYNC 每次write都等待物理I/O完成,但是如果写操作不影响读取刚写入的数据,则不等待文件属性更新。
fdatasync(在每次提交时调用fdatasync())
fsync(在每次提交时调用fsync())
fsync的性能问题,与fdatasync
除了同步文件的修改内容(脏页),fsync还会同步文件的描述信息(metadata,包括size、访问时间st_atime& st_mtime等等),因为文件的数据和metadata通常存在硬盘的不同地方,因此fsync至少需要两次IO写操作fsync_writethrough(在每次提交时调用fsync(),强制任何磁盘写高速缓存的直通写)多余的一次IO操作,有多么昂贵呢?根据Wikipedia的数据,当前硬盘驱动的平均寻道时间(Average seek time)大约是3~15ms,7200RPM硬盘的平均旋转延迟(Average rotational latency)大约为4ms,因此一次IO操作的耗时大约为10ms左右。这个数字意味着什么?
open_sync(用open()选项O_SYNC写WAL 文件)
O_SYNC,以同步方式写入文件
功能:强制刷新内核缓冲区到输出文件。这是有必要的,因为为了数据安全,需要确保将数据真正写入磁盘或者磁盘的硬件告诉缓存中。
同步IO的定义:某一IO操作,要么已成功完成到磁盘的数据传递,要么被诊断为不成功。
              
full_page_writes = on      
全页写
wal_compression = off 
当这个参数为on时,如果full_page_writes为打开或者处于基础备份期间,PostgreSQL服务器 会压缩写入到 WAL 中的完整页面镜像。

 
wal writerprocess 相关参数浅析 3
wal_log_hints = off 
当这个参数为on时,PostgreSQL服务器一个检查点之后页面被第一次修改期间把该磁盘页面的整个内容都写入WAL,即使对所谓的提示位做非关键修改也会这样做。
                 
wal_buffers = -1
用于还未写入磁盘的 WAL 数据的共享内存量。默认值 -1 选择等于shared_buffers的 1/32 的尺寸(大约3%),但是不小于64kB也不大于WAL 段的尺寸(通常为)。如果自动的选择太大或太小可以手工设置该值,但是任何小于32kB的正值都将被当作32kB。如果指定值时没有单位,则以WAL块作为单位,即为 XLOG_BLCKSZ 字节,通常为8kB。这个参数只能在服务器启动时设置。 
wal_writer_delay = 200ms
指定 WAL 写入器刷写WAL 的频繁程度,以时间为单位。 在刷写WAL之后,写入器将根据wal_writer_delay所给出的时间长度进行睡眠,除非被一个异步提交的事务提前唤醒。如果最近的刷写发生在 wal_writer_delay 之前,并且小于 wal_writer_flush_afterWAL的值产生之后,那么WAL只会被写入操作系统,而不会被刷写到磁盘。如果指定值时没有单位,则以毫秒作为单位。默认值是 200 毫秒(200ms)。注意在很多系统上,有效的睡眠延迟粒度是10 毫秒,把wal_writer_delay设置为一个不是 10 的倍数的值,其效果和把它设置为大于该值的下一个 10 的倍数产生的效果相同。这个参数只能在postgresql.conf文件中或者服务器命令行上设置。

 

autovacuumlauncher process /workers 概述 1

在PostgreSQL中,对表元组的update或delete操作并未立刻删除旧版本的数据,表中的旧元组只是被标识为删除状态,并未
立即释放空间。这种处理对于获取多版本并发控制是必要的,如果一个元组的版本仍有可能被其它事物看到,那么就不能
删除元组的该版本。当事物提交后,过期元组版本将对事物不再有效,因而其占据的空间必须回收以供其他新元组使用。
以避免对磁盘空间增长的无休止需求,此时对数据库的清理工作通过运行vacuum来实现。从8.1版本开始,数据库引入一个
额外的后台进程 autoVacuum,自动执行vacuum和analyze命令,回收被标识为删除状态记录的空间,更新表的统计信息。
系统自动清理进程分为两个不同的进程
1.Autovacuum launcher
用于收集数据库运行信息,根据数据库选择规则选中一个数据库,并调度一个Autovacuum workers进程执行清理操作。
2. Autovacuum workers
执行清理任务,Launcher进程中维护有Worker进程列表,空闲的Worker列表,正在启动的Worker列表,运行中的Worker进程,
Launcher进程在不同状态之间的切换实现了Worker进程的调度工作。

 

autovacuumlauncher process /workers 概述 2

删除或重用无效元组的磁盘空间
更新数据统计信息,保证执行计划更优
更新visibility map,加速index-onlyscans
避免XID 回卷造成的数据丢失

 

autovacuumlauncher process /workers 源码浅析

src/backend/postmaster/autovacuum.c
* Main loop for the autovacuum launcherprocess.
*autovacuum进程主循环
*/
NON_EXEC_STATIC void
AutoVacLauncherMain(int argc, char *argv[])
{* 在某个worker仍然在启动时,不能启动新的worker,因此休眠一段时间;
* 另外一个worker在ready后会第一时间唤醒我们.
* 只需要等待autovacuum_naptime参数设置的时间(单位秒)(最大为60s).
* 注意,在这里不能够连接一个特定的数据库不存在任何问题,因为worker在
* 尝试连接时,通过startingWorker指针销毁自己.
* 通过postmaster检测到问题(如fork()失败)会报告并且进行不同的处理,
* 这里唯一的问题是可能导致这里的处理逻辑在AutoVacWorkerMain的早起触发错误,
* 而且是在worker通过startingWorker指针清除WorkerInfo前.
*/
waittime = Min(autovacuum_naptime, 60) * 1000;
if (TimestampDifferenceExceeds(worker->wi_launchtime, current_time,
waittime))
{}
… …
}
复制

 


autovacuumlauncher process /workers 相关参数浅析

每当死亡元组(dead tuple)超过以下公式时,就会触发自动清理
autovacuum_vacuum_threshold +pg_class.reltuples *
autovacuum_vacuum_scale_factor
其中两个参数分别为:
autovacuum_vacuum_threshold = 50 #阈值
autovacuum_vacuum_scale_factor = 0.2 #比例因子
死亡元组数可以认为是pg_stat_all_tables中n_dead_tup的值


触发autovacuum的消耗
autovacuum的清理过程是从数据文件中读取页面(默认8kB数据块),并检查它是否需要清理,如果没有死亡元组,页面就会被丢弃而不做任何更改,否则它被清理(死元组被删除),被标记为“脏页”并最终写出来。成本核算基于postgresql.conf定义三个参数:
vacuum_cost_page_hit = 1 #如果页面是从shared_buffers读取的,则计为1
vacuum_cost_page_miss = 10 #如果在shared_buffers找不到并且需要从操作系统中读取,
则计为10(它 可能仍然从RAM提供,但我们不知道)
vacuum_cost_page_dirty = 20 #当清理修改一个之前干净的块时需要花费的估计代价,它表示
再次把脏块刷 出到磁盘所需要的额外I/O,默认值为20
 
再加上另外两个参数即可计算出清理操作的成本:
autovacuum_vacuum_cost_delay = 20ms #每次完成清理后睡眠20ms
autovacuum_vacuum_cost_limit = 200 #完成一次清理的消耗限制

 

stats collectorprocess 概述

Stats进程是PostgreSQL数据库系统的统计信息收集器,专门负责收集数据库系统运行中的统计信息,如在一个表和索引上进行了多少次插入与更新操作、磁盘块的数量和元组的数量、每个表上最近一次执行清理和分析操作的时间,以及统计每个用户自定义函数调用执行时间等。
系统表pg_statistic中存储了stats收集的各类统计信息,另外在数据库集簇的目录下有与统计信息收集器相关的文件,pg_stat_tmp文件则是stat进程和各个后台进程,进行交互的临时文件所在地。

 


stats collectorprocess 源码浅析

src/backend/postmaster/pgstat.c
复制
从 postmaster 启动时或存在的 collector 进程死掉是调用。试图启动一个新的statisticscollector。

int
pgstat_start(void)
{
time_t curtime;
pid_t pgStatPid;
复制
   /* 检查套接字是否存在,否则 pgstat_init 失败,而且无能为力。*/
 if (pgStatSock == PGINVALID_SOCKET)
return 0;
复制
   /* 如果上次 collector 启动后不久,就什么都不做。这是一个安全阀,为了防止collector 在启动的时候死掉,连续重复尝试,注意,因为我们将从postmaster main loop 重新调用,我们稍后会得到另一个机会启动。*/

   curtime = time(NULL);
if ((unsigned int) (curtime - last_pgstat_start_time) <
(unsigned int) PGSTAT_RESTART_INTERVAL)
return 0;
last_pgstat_start_time = curtime;
复制

 

bgworker:logical replication launcher 概述

logical replication launcher 逻辑复制启动器
逻辑复制是PostgreSQL V10重量级新特性,支持内置的逻辑复制。在10版本之前,虽然没有内置的逻辑复制,也可以通过其它方式实现,例如触发器、自定义脚本实现表级别同步,另外也可以通过外部工具Londiste3 实现。
从2014年发布的9.4版本开始,PostgreSQL就支持逻辑复制了,只是一直没有将其引入内核。
你可以针对同一个数据库实例,同时使用逻辑复制和物理复制,因为他们都是基于REDO的。
逻辑复制应用场景可基于表级别复制,是一种粒度可细的复制,主要用在以下场景:
1)满足业务上需求,实现某些指定表数据同步
2)报表系统,采集报表数据
3)PostgreSQL 跨版本数据同步
4)PostgreSQL 大版本升级
5)可从多个上游服务器,做数据的聚集和合并
逻辑复制原理
使用发布者/订阅者模型,使用订阅复制槽技术,可并行的传输WAL日志,通过在订阅端回放WAL日志中的逻辑条目,保持复制表的数据同步,这里不是“SQL”复制,而是复制SQL操作的结果。
 

bgworker:logical replication launcher 相关参数浅析

subscription - 订阅相关配置
这些设置控制逻辑复制订阅的行为。发布者的值无关紧要。
请注意,wal_receiver_timeout和 wal_retrieve_retry_interval配置参数还影响逻辑复制工作。
max_logical_replication_workers (int)
指定逻辑复制工作的最大数量。这包括应用工作和表同步工作。
逻辑复制工作进程是从max_worker_processes 定义的进程池中取出的。
默认值是4。
max_sync_workers_per_subscription (integer)
每个订阅的最大同步工作者数量。此参数控制订阅初始化期间或添加新表时初始数据副本的并行数量。
目前,每个表只能有一个同步工作进程。
同步工作进程是从max_logical_replication_workers 定义的进程池中取出的。默认值是2。
3)Replication Slots- 复制槽(发布端)
每个(活动)订阅都从远程(发布)端的复制槽接收更改。
通常,使用CREATE SUBSCRIPTION 创建订阅时会自动创建远程复制槽,使用DROP SUBSCRIPTION 删除订阅时会自动删除该槽。
复制槽提供了一种自动化的方法来确保主控机在所有的后备机收到 WAL段之前不会移除它们,主库随时知道从库应用wal 的情况 , 哪怕从库掉线,主库依然保留 wal日志这种机制的缺点是,如果从库掉线很久, 那么主库的wal日志 会一直保留以至于撑暴硬盘, 这时监控需要做到位。
Replication Slots - 复制槽(发布端)
#postgresql.conf关联配置
wal_level = logical
max_replication_slots = 10  #max_replication_slots 值最少需设置成 1,设置后重启数据库生效。

 

WALSender/reveiver 概述

后台进程walsender,该进程实质上是streaming replication环境中master节点上普通
的backend进程,在standby节点启动时,standby节点向master发送连接请求,master节点
的postmaster进程接收到请求后,启动该进程与standby节点的walreceiver进程建立通
讯连接,用于传输WAL Record.



WALSender/reveiver 源码浅析1

PostgresMain
后台进程postgres的主循环入口 — 所有的交互式或其他形式的后台进程在这里启动.
其主要逻辑如下:
1.初始化相关变量
2.初始化进程信息,设置进程状态,初始化GUC参数
3.解析命令行参数并作相关校验
4.如为walsender进程,则调用WalSndSignals初始化,否则执行其他信号初始化
5.初始化BlockSig/UnBlockSig/StartupBlockSig
6.非Postmaster,则检查数据库路径/切换路径/创建锁定文件等操作
7.调用BaseInit执行基本的初始化
8.调用InitProcess/InitPostgres初始化进程
9.重置内存上下文,处理加载库和前后台消息交互等
10.初始化内存上下文
11.进入主循环
11.1切换至MessageContext上下文
11.2初始化输入的消息
11.3给客户端发送可以执行查询等消息
11.4读取命令
11.5根据命令类型执行相关操作



 

WALSender/reveiver 源码浅析2

void
PostgresMain(int argc, char *argv[],
const char *dbname,
const char *username)
{
/*
* Set up signal handlers and masks.
* 配置信号handlers和masks.
*
* Note that postmaster blocked all signals before forking child process,
* so there is no race condition whereby we might receive a signal before
* we have set up the handler.
* 注意在fork子进程前postmaster已阻塞了所有信号,
* 因此就算接收到信号,但在完成配置handler前不会存在条件争用.
*
* Also note: it's best not to use any signals that are SIG_IGNored inthe
* postmaster. If such a signalarrives before we are able to change the
* handler to non-SIG_IGN, it'll get dropped. Instead, make a dummy
* handler in the postmaster to reserve thesignal. (Of course, this isn't
* an issue for signals that are locally generated, such as SIGALRM and
* SIGPIPE.)
* 同时注意:最好不要使用在postmaster中标记为SIG_IGNored的信号.
* 如果在改变处理器为non-SIG_IGN前,接收到这样的信号,会被清除.
* 相反,可以在postmaster中创建dummy handler来保留这样的信号.
* (当然,对于本地产生的信号,比如SIGALRM和SIGPIPE,这不会是问题)
*/
if (am_walsender)//wal sender进程?
WalSndSignals();//如果是,则调用WalSndSignals
else//不是wal sender进程
{
//设置标记,读取配置文件
pqsignal(SIGHUP, PostgresSigHupHandler); /* set flag to read config
* file */
复制

 



WALSender/reveiver 源码浅析3

//中断信号处理器(中断当前查询)
pqsignal(SIGINT, StatementCancelHandler); /* cancel current query */
//终止当前查询并退出
pqsignal(SIGTERM, die); /* cancel current query and exit */
/*
* In a standalone backend, SIGQUIT can be generated from the keyboard
* easily, while SIGTERM cannot, so we make both signals do die()
* rather than quickdie().
* 在standalone进程,SIGQUIT可很容易的通过键盘生成,而SIGTERM则不好生成,
* 因此让这两个信号执行die()而不是quickdie().
*/
//bool IsUnderPostmaster = false
if (IsUnderPostmaster)
//悲催时刻,执行quickdie()
pqsignal(SIGQUIT, quickdie); /*hard crash time */
else
//执行die()
pqsignal(SIGQUIT, die); /* cancel current query and exit */
//建立SIGALRM处理器
InitializeTimeouts(); /* establishesSIGALRM handler */
/*
* Ignore failure to write to frontend. Note: if frontend closes
* connection, we will notice it and exit cleanly when control next
* returns to outer loop. Thisseems safer than forcing exit in the
* midst of output during who-knows-what operation...
* 忽略写入前端的错误.
* 注意:如果前端关闭了连接,会通知并在空中下一次返回给外层循环时退出.
* 这看起来会比在who-knows-what操作期间强制退出安全一些.
*/
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, procsignal_sigusr1_handler);
pqsignal(SIGUSR2, SIG_IGN);
pqsignal(SIGFPE, FloatExceptionHandler);
复制

 

WALSender/reveiver 相关参数浅析1

主库

wal_level = logical

max_wal_senders = 32

max_standby_archive_delay = 300s

max_standby_streaming_delay = 300s   

当热后备机处于活动状态时,这个参数决定取消那些与即将应用的 WAL 项冲突的后备机查询之前,后备服务器应该等待多久

wal_receiver_status_interval = 10s 

指定在后备机上的 WAL 接收者进程向主服务器或上游后备机发送有关复制进度的信息的最小频度,它可以使用pg_stat_replication视图看到。后备机将报告它已经写入的上一个预写式日志位置、它已经刷到磁盘的上一个位置以及它已经应用的最后一个位置。这个参数的值是报告之间的最大时间量。每次写入或刷出位置改变时会发送状态更新,或者至少按这个参数的指定的频度发送。因此,应用位置可能比真实位置略微滞后。如果指定值时没有单位,则以秒为单位。默认值是10 秒。将这个参数设置为零将完全禁用状态更新。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。   

hot_standby_feedback = on

指定一个热后备机是否将会向主服务器或上游后备机发送有关于后备机上当前正被执行的查询的反馈。这个参数可以被用来排除由于记录清除导致的查询取消,但是可能导致在主服务器上用于某些负载的数据库膨胀。反馈消息的发送频度不会高于每个wal_receiver_status_interval周期发送一次。默认值是off。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。

hot_standby = on

指定在恢复期间,你是否能够连接并运行查询,默认值是on。这个参数只能在服务器启动时设置。它只在归档恢复期间或后备机模式下才有效。

 



WALSender/reveiver 相关参数浅析2

备库
12之前
recovery.conf
recovery_target_timeline='latest‘
standby_mode=on
primary_conninfo='host=vipm port=5432user=replica password=REPLICA321 keepalives_idle=60‘
12之后
recovery.conf不再使用,如果该文件存在,服务器将无法启动。recovery.signal和standby.signal文件现在用于切换到非主模式。trigger_file设置已更名为promote_trigger_file。删除了standby_mode设置。
要启动服务器为目标恢复模式,需在数据目录中建立名为recovery.signal的文件。如果同时创建了standby.signal 和 recovery.signal文件,则优先使用备用模式。目标恢复模式在归档的WAL全部回放或到达recovery_target时结束。

 



archiverprocess 概述

PostgreSQL 从8.x版本开始提出PITR(Point-In-Time-Recovery)技术,支持将数据库恢复到其运行历史中任意一个有记录的时间点,PITR重要的基础就是对WAL文件的归档功能,archiver辅助进程的目标就是对WAL日志在磁盘上的存储形式(Xlog)进行归档备份。
 
PostgreSQL在数据集簇的Pg_wal子目录中始终只使用一个wal日志文件,这个日志文件记录数据库中数据文件的每个改变,归档日志+当前WAL形成连续的WAL记录,用作PITR,为了给数据库管理员提供最大的灵活性,PostgreSQL不对如何归档做任何假设,而是让DBA提供一个shell命令来拷贝一个完整的WAL 段文件到备份存储位置。该命令可以就是一个CP命令,或者是一个复杂的shell脚本,所有操作都由管理员决定。
 
为了标识各个段文件的状态,PostgreSQL在数据集簇的pg_wal/archiver_stats目录下记录了每一个wal段文件的状态文件,状态文件的前缀与段文件同名,以标识时间顺序的整数形式命名。状态文件后缀为.ready或者.done,代表段文件的归档状态,分别代表”就绪”和”已完成”两种模式。Archiver进程会找到所有状态为”就绪”的段文件,找到状态文件后,若设置了归档命令,archiver进程将归档命令解析后交由操作系统的shell函数system执行,文件归档成功后pg_wal/archiver_stats目录下相应的状态文件后缀改为.done.


 

archiverprocess 源码浅析

src/backend/postmaster/pgarch.c
/*
*pgarch_start
*
在启动时或现有archiver died从postmaster处调用。尝试启动一个新的归档进程。返回子进程的PID,
如果失败则返回0。注意:如果失败,将从postmaster主循环再次调用我们。
*/
int
pgarch_start(void)
{
time_t curtime;
pid_t pgArchPid;
/*
如果从上次归档开始太快,请不要执行任何操作。这是一个安全阀,用于防止在执行时archiver立即dying 的情况下持续重生。
请注意,因为我们将从postmaster主循环重新调用,所以稍后我们将获得另一个机会。*/
curtime= time(NULL);
if((unsigned int) (curtime - last_pgarch_start_time) <
(unsignedint) PGARCH_RESTART_INTERVAL)
return0;
last_pgarch_start_time= curtime;
… …
复制

 


archiverprocess 相关参数浅析

archive_mode = on              
archive_command = 'DIR=/opt/arch/`date+%F`; test ! -d $DIR && mkdir -p $DIR; chmod 755 $DIR; test ! -f$DIR/%f && cp %p $DIR/%f; chmod 755 $DIR/%f'               
archive_command 中配置SCP命令可以把归档文件,拷贝到远端服务器进行备份。
非主库清理归档配置
 
archive_cleanup_command ='pg_archivecleanup -d /opt/peer_arch/`date +%F` %r 2>>cleanup.log‘
本参数定义了在每个restart point时所执行的shell命令。
archive_cleanup_command参数的目的是提供一个清理不再被standby server所需要的老的archived wal file的机制。
restart point 是一个 point,该point用于standby server重启recovery操作。

 



logger process 概述

日志信息是数据库管理员获取数据库系统运行状态的有效手段。在数据库出现故障时, 日志信息非常有用的。把数据库日志信息集中输出到一个位置将极大方便管理员维护数据库系统,然而,日志输出将产生大量数据,单文件保存时不利于日志文件操作。因此,在SysLogger的配置选项中可以设置日志文件的大小,SysLogger会在日志文件达到指定大小时关闭当前日志文件,产生新的日志文件。




logger process 源码浅析

src/backend/postmaster/syslogger.c
/* syslogger process 主入口 */
NON_EXEC_STATIC void
SysLoggerMain(int argc, char *argv[])
{
#ifndef WIN32
char logbuffer[READ_BUF_SIZE];
int bytes_in_logbuffer =0;
#endif
char *currentLogDir;
char *currentLogFilename;
int currentLogRotationAge;
pg_time_t now;
now = MyStartTime;
#ifdef EXEC_BACKEND
syslogger_parseArgs(argc, argv);
#endif /* EXEC_BACKEND */
am_syslogger = true;
init_ps_display("logger", "", "","");
/* 如果重新启动,我们的STDRR已经被重定向到我们自己的输入管道中。这当然是无用的,
更不用说它干扰检测管EOF。把stderr只想到/dev/null 。假设 syslogger 中生成的所有的消息将通过
elog.c发送到 write_syslogger_file 。*/
if (redirection_done)
{
int fd = open(DEVNULL,O_WRONLY, 0);
/* 关闭可能看起来是多余,但实际上并不是:我们要确保管道即使打开失败也会关闭。我们可以在stderr 指向任何地方运行,但我们不能负担额外的管道输入描述符。由于我们只是试图将这些重置为DEVNULL,所以在这里从close/dup2调用中检查失败没有多大意义,如果它们失败,那么可能文件描述符被关闭,并且任何写入都将进入 bitbucket 。*/
复制

 


logger process 相关参数浅析

log_destination = 'csvlog';

logging_collector = on;

log_directory = 'pg_log';

log_filename = 'postgresql-log.%a';

log_truncate_on_rotation = on;

当logging_collector被启用时,这个参数将导致PostgreSQL截断(覆盖而不是追加)任何已有的同名日志文件。不过,截断只在一个新文件由于基于时间的轮转被打开时发生,在服务器启动或基于尺寸的轮转时不会发生。如果被关闭,在所有情况下以前存在的文件将被追加。例如,使用这个设置和一个类似postgresql-%H.log的log_filename将导致产生 24 个每小时的日志文件,并且循环地覆盖它们。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。

log_rotation_age = 1440;

在打开了logging_collector的时候,这个选项设置一个独立日志文件的最大生存期。在数值指定的分钟过去之后,将创建一个新的日志文件。设置为零可以关闭以时间为基础的新日志文件的创建。在打开了logging_collector的时候,这个选项设置一个独立日志文件的最大生存期。在数值指定的分钟过去之后,将创建一个新的日志文件。设置为零可以关闭以时间为基础的新日志文件的创建。

log_rotation_size = 1000000;

在打开了logging_collector的时候,这个选项设置一个独立的日志文件的最大尺寸。在数值指定的千字节写入日志文件之后,将会创建一个新的日志文件。设置为零可以关闭以尺寸为基础的新日志文件的创建。

log_min_duration_statement = 500;

在打开了logging_collector的时候,这个选项设置一个独立的日志文件的最大尺寸。在数值指定的千字节写入日志文件之后,将会创建一个新的日志文件。设置为零可以关闭以尺寸为基础的新日志文件的创建。如果某个语句的持续时间大于或者等于这个毫秒数,那么在日志行上记录该语句及其持续时间。设置为零将打印所有查询和他们的持续时间。设置为-1(缺省值)关闭这个功能。比如,如果你把它设置为250ms,那么所有运行时间等于或者超过 250ms 的SQL 语句都会被记录。打开这个选项可以很方便地跟踪需要优化的查询。只有超级用户可以改变这个设置。对于使用扩展查询协议的客户端,语法分析、邦定、执行每一步所花时间都分别记录。

log_statement = ddl;

控制记录哪些SQL语句。有效的值是none(off), ddl, mod和 all (所有语句)。ddl记录所有数据定义命令,比如CREATE, ALTER和DROP语句。mod记录所有ddl语句,加上数据修改语句INSERT,UPDATE, DELETE, TRUNCATE, 和COPY FROM。如果所包含的命令类型吻合,那么PREPARE, EXECUTE和EXPLAIN ANALYZE语句也同样被记录。对于使用扩展查询协议的客户端,记录发生在接受到扩展信息并包含邦定参数(内置单引号要双写)的时候。

缺省是none。只有超级用户可以改变这个设置。




PG考试本年度

最后一场将在11月20日举行

想考试和培训的同学

可以扫描下方二维码咨询老师


往期回顾






【干货分享】PostgreSQL技术文章:Database Cluster Table



腾讯云数据库TDSQL精英挑战赛邀您参加~ 35w参赛奖金坐等你来



【干货分享】深度解析Write-Ahead Logging


点击“阅读原文”

文章转载自云贝教育,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论