首页 > 数据库 > 数据库开发技术 > PostgreSQL与信号量的爱恨纠葛
众所周知,PostgreSQL对数据库的操作与操作系统高度内联。现在,在我们深入研究案例的细节之前,我已经完成了操作系统的系统调用和PostgreSQL后台进程的kill信号,让我们了解里面有什么。

进程是一段程序的执行实例。它在操作系统中执行特定的任务。每一个进程都会被分配一个唯一的数字,也称之为进程标识符(PID),这个整型数字介于2到32768之间。当启动一个进程时,分配的数字会从2开始,数字1通常是为init进程保留的。1号进程负责管理其他进程。
进程表负责描述当前所有已加载的进程。通过ps命令可以展示相关进程。默认情况下,ps只显示与终端、控制台、串行线或伪终端保持连接的进程。
为了查看所有的进程,可以使用-e或者 -f的选项获取完整信息,比如 ps -ef
postgres 28056 1 0 21:51 pts/0 00:00:01 /usr/local/pgsql/bin/postgres -D /u02/pgsql/data
在这个例子中,1是init进程,28056是子进程。
系统调用有时又被称为内核调用,是在类unix操作系统中,活跃进程通过软中断对内核发出服务请求。
我们可以通过调用fork()来创建一个新的进程。这个系统调用会复制当前进程,并在进程表中插入一个新的记录,进程表中有许多其他一样的属性记录。只是,新创建的进程会是调用fork()的进程的子进程。新创建进程的PPID对应父进程的PID。
信号即一个通知,由操作系统或某个应用发送给程序的消息。
一个信号可能由:
内核发给某个进程
一个进程发给另一个进程
某个进程发给自己
根据信号类型,信号会向特定进程发出某些事件的警告。
Linux内核实现了约30个信号,每个信号由一个数字标识,介于1到31之间。
比如,SIGKILL或者信号9,告诉程序某人正在尝试干掉它。现在,让我们将Linux的进程及信号与PostgreSQL关联起来。
启动PostgreSQL时,第一个启动的进程时Postmaster。然后由Postmaster创建其他后台进程。如果你使用pstree命令来查看各个进程之间的关系,你会发现Postmaster进程是所有进程的父进程。Postmaster启动时,它需要知道数据的存储区域。具体位置需要通过 -D 选项或者PGDATA的环境变量来指定。
[postgres@asristgdb ~]$ ps -ef | grep postgres
postgres 7913 1 0 23:11 pts/3 00:00:00 /usr/local/pgsql/bin/postgres -D /u02/pgsql/data
postgres 7914 7913 0 23:11 ? 00:00:00 postgres: logger process
postgres 7916 7913 0 23:11 ? 00:00:00 postgres: checkpointer process
postgres 7917 7913 0 23:11 ? 00:00:00 postgres: writer process
postgres 7918 7913 0 23:11 ? 00:00:00 postgres: wal writer process
postgres 7919 7913 0 23:11 ? 00:00:00 postgres: autovacuum launcher process
postgres 7920 7913 0 23:11 ? 00:00:00 postgres: stats collector process
在这里,进程7913是Postmaster进程,它创建了多个被称之为后台工作进程的子进程。如你所见,这些后台工作进程的PPID是Postmaster进程的PID。Postmaster进程也负责创建后台进程,用于客户端连接。建议阅读一下:PostgreSQL的连接时如何建立的。
[postgres@asristgdb ~]$ psql -d testdb -U testuser
psql (9.6.8)
Type "help" for help.
testdb=#
Process Output:
ps -ef | grep postgres (output truncated)
postgres 7994 7913 0 23:11 ? 00:00:00 postgres: testuser testdb [local] idle
我们可以通过pstree查看进程列表:
[postgres@asristgdb ~]$ pstree -p 7913
postgres(7913)¬postgres(7914)
postgres(7916)
postgres(7917)
postgres(7918)
postgres(7919)
postgres(7920)
[postgres@asristgdb ~]$
PostgreSQL使用以下操作系统信号对数据库进程发起调用。
ABRT
INT
QUIT
TERM
HUP
USR1
USR2
下面列表描述了每个信号的作用。
我将上面表格中提到的所有信号发送给Postmaster以及其他后台进程,并捕获对应的结果。下表显示了当我向Postmaster进程发送KILL信号时系统的对应行为。
语法:pg_ctl kill SIGNALNAME PID
比如:/usr/local/pgsql/bin/pg_ctl kill INT 20958,20958是Postmaster进程的PID。
我还将相同的信号发给其他后台进程,结果如下:
如上所述,kill -9不仅会终止单个进程,而且还会重新初始化其他所有进程。
任意一个进程被 kill -9终止后,Postmaster都会重置其他后台工作进程。
让我们看个例子,理解一下这个观点:
[postgres@postgreshelp ~]$ ps -ef | grep postgres
postgres 7913 1 0 Feb01 ? 00:00:01 /usr/local/pgsql/bin/postgres -D /u02/pgsql/data
postgres 7914 7913 0 Feb01 ? 00:00:00 postgres: logger process
postgres 7916 7913 0 Feb01 ? 00:00:00 postgres: checkpointer process
postgres 7917 7913 0 Feb01 ? 00:00:05 postgres: writer process
postgres 7918 7913 0 Feb01 ? 00:00:03 postgres: wal writer process
postgres 7919 7913 0 Feb01 ? 00:00:06 postgres: autovacuum launcher process
postgres 7920 7913 0 Feb01 ? 00:00:03 postgres: stats collector process
postgres 20568 7913 0 20:28 ? 00:00:00 postgres: testuser testdb [local] idle
这里,20568是某个空闲的客户端连接的PID,现在使用kill -9干掉该会话:
[postgres@asristgdb ~]$ kill -9 20568
[postgres@asristgdb ~]$
捕获的日志如下:
LOG: server process (PID 20568) was terminated by signal 9: Killed
LOG: terminating any other active server processes
WARNING: terminating connection because of crash of another server process
DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.
HINT: In a moment you should be able to reconnect to the database and repeat your command.
LOG: all server processes terminated; reinitializing
LOG: database system was interrupted; last known up at 2019-02-01 23:11:41 IST
LOG: database system was not properly shut down; automatic recovery in progress
LOG: invalid record length at 41/E0D53F60: wanted 24, got 0
LOG: redo is not required
LOG: checkpoint starting: end-of-recovery immediate
LOG: checkpoint complete: wrote 0 buffers (0.0%); 0 transaction log file(s) added, 0 removed, 0 recycled; write=0.026 s, sync=0.000 s, total=0.029 s; sync files=0, longest=0.000 s, average=0.000 s; distance=0 kB, estimate=0 kB
LOG: MultiXact member wraparound protections are now enabled
LOG: database system is ready to accept connections
LOG: autovacuum launcher started
Postmaster进程也会重启所有的后台工作进程,我们可以使用ps 命令确认:
[postgres@asristgdb ~]$ ps -ef | grep postgres
postgres 20730 7913 0 20:30 ? 00:00:00 postgres: checkpointer process
postgres 20731 7913 0 20:30 ? 00:00:00 postgres: writer process
postgres 20732 7913 0 20:30 ? 00:00:00 postgres: wal writer process
postgres 20733 7913 0 20:30 ? 00:00:00 postgres: autovacuum launcher process
postgres 20734 7913 0 20:30 ? 00:00:00 postgres: stats collector process
每个进程分配了一个新的进程ID。
有时重置可能会导致损坏或错误。因此,强烈建议不要使用kill -9杀掉任何一个PostgreSQL进程,包括空闲进程。
作为替代,我们可以使用pg_cancen_backend(pid)或pg_terminate_backend(pid)指定进程PID,发送相应的信号(分别对应SIGINT和SIGTERM)。
Postmaster是每一个PostgreSQL进程的父进程
如果因为某个原因停止了,Postmaster会重启其他后台工作进程
Postmaster针对每个客户请求创建一个新的进程
在你的环境里不要使用kill -9
PostgreSQL目前还比较简陋没有类似Oracle的pmon进程,Oracle由于pmon进程的存在,在某个进程非正常退出后,会做好妥善的后事及清理工作。PostgreSQL由于没有,假如某个进程正在修改shared buffer里的某个数据块,然后被异常终止了,Postmaster为了安全,就会主动停下其他后台进程,做修复善后工作,改头换面再接受连接,所以老老实实用select pg_terminate_backend()和pg_cancen_backend()吧,要用也用kill(不带-9 )的那种。
另外对于我们常见的关闭PostgreSQL服务的方式中,-mi的方式,其实也用到了SIGKILL,假如5s还没有干掉你,那就只能sigkill了。
This is the Immediate Shutdown mode. The server will send SIGQUIT to all child processes and wait for them to terminate. If any do not terminate within 5 seconds, they will be sent SIGKILL. The master server process exits as soon as all child processes have exited, without doing normal database shutdown processing. This will lead to recovery (by replaying the WAL log) upon next start-up. This is recommended only in emergencies.
附所有信号:(来源:http://c.biancheng.net/view/3482.html)
原文作者:PostgreSQL学徒;如有侵权,请联系管理员删除。