前言
本来来自postgresweekly (2024.1.17那一期)推荐的技术文章。重新加工整理,并加入本人自己的一些附加实验。总之一句话:KILL -9 戾气太重,千万不要在PostgreSQL生产环境当中使用。后果非常严重。
以下是那篇文章的原文。kill -9 explained for PostgreSQL[1]
是否要终止数据库连接? 或者你想用kill -9? 在你的PostgreSQL数据库服务器上?在你这么做之前,有些事情你应该知道。因此,我们想在这篇博客中回答的问题是: 我如何正确地终止PostgreSQL进程?
1.杀死PostgreSQL进程
在我们讨论进程终止之前,首先看一下操作系统级别的简单PostgreSQL实例/集群是有意义的:
1200474 ? Ss 0:00 /usr/pgsql-15/bin/postgres -D sample_db
1200475 ? Ss 0:00 _ postgres: logger
1200476 ? Ss 0:00 _ postgres: checkpointer
1200477 ? Ss 0:00 _ postgres: background writer
1200479 ? Ss 0:00 _ postgres: walwriter
1200480 ? Ss 0:00 _ postgres: autovacuum launcher
1200481 ? Ss 0:00 _ postgres: logical replication launcher
我们在这里看到的是postmaster进程和它的子进程,它们共同构成了PostgreSQL的核心系统基础设施。这个基础架构中的大多数进程都可以很好地终止,没有任何问题:
[sql gutter="false"]
[hs@sample ~]$ kill 1200480
我们刚刚毁掉的是autovacuum进程。这完全不是问题,因为postmaster父进程会立即启动一个新的子进程: 我们可以通过新的进程ID来判断,这个进程ID可以在进程表中看到。
1.1 PostgreSQL基本上会确保这个东西总是在运行:
1200474 ? Ss 0:00 /usr/pgsql-15/bin/postgres -D sample_db
1200475 ? Ss 0:00 _ postgres: logger
1200476 ? Ss 0:00 _ postgres: checkpointer
1200477 ? Ss 0:00 _ postgres: background writer
1200479 ? Ss 0:00 _ postgres: walwriter
1200481 ? Ss 0:00 _ postgres: logical replication launcher
1200524 ? Ss 0:00 _ postgres: autovacuum launcher
为了更好地理解这一点,我们必须了解kill命令的作用。它实际上并不杀死任何东西,它向操作系统进程发送一个信号。名称“kill”仍然是合适的,因为目标进程没有显式处理的任何信号都将导致其立即终止。
1.2 SIGTERM信号 与 kill -9的对比
请记住,我们在这里使用了**“正常”kill(它发送一个SIGTERM信号)。然而,并不是所有的kill都是一样的。“正常”的kill可以被应用程序捕获并处理。基本上发生的事情是,auto vacuum启动进程已经捕获了SIGTERM信号并正确地终止了自己**。“适当”意味着所有共享资源都保持一致、干净的状态,并且没有剩余资源可以通过系统在某个随机位置引起问题。然而,普通的kill并不是kill -9所做的。问题是:“-9”发送SIGKILL信号,目标进程无法捕获和处理;它只是被操作系统粗暴地销毁,可能使共享内存和其他资源处于不一致的状态。这是在终止进程时必须牢记的一个极其重要的区别。
2. 以"-9"的方式杀掉数据库连接
但是如果我们残忍地终止一个数据库连接会发生什么呢?最后一个进程是到PostgreSQL的标准连接。用户hs通过本地IPv6连接连接到postgres数据库。详情如下:
1200474 ? Ss 0:00 /usr/pgsql-15/bin/postgres -D sample_db
1200475 ? Ss 0:00 _ postgres: logger
1200476 ? Ss 0:00 _ postgres: checkpointer
1200477 ? Ss 0:00 _ postgres: background writer
1200479 ? Ss 0:00 _ postgres: walwriter
1200481 ? Ss 0:00 _ postgres: logical replication launcher
1200524 ? Ss 0:00 _ postgres: autovacuum launcher
1200623 ? Ss 0:00 _ postgres: hs postgres ::1(40498) idle
2.1 杀掉一些进程
用户连接被粗暴地消除了,但是仔细看看发生了什么:
[hs@sample ~]$ kill -9 1200623
[hs@sample ~]$ ps axf | grep post
1200474 ? Ss 0:00 /usr/pgsql-15/bin/postgres -D sample_db
1200475 ? Ss 0:00 _ postgres: logger
1200628 ? Ss 0:00 _ postgres: checkpointer
1200629 ? Ss 0:00 _ postgres: background writer
1200630 ? Ss 0:00 _ postgres: walwriter
1200631 ? Ss 0:00 _ postgres: autovacuum launcher
1200632 ? Ss 0:00 _ postgres: logical replication launcher
2.2 大多数进程都消失了,并使用新的进程ID重新启动。为什么呢?
前面,我提到“-9”发送一个不可捕获的信号,这意味着进程不能对kill作出反应。因此,进程可能在任何时候终止,这反过来意味着我们不能依赖于它使数据库保持一致状态的事实。这个过程可能会让我们处于任何状态。操作可能在共享内存、磁盘或其他任何地方发生更改时被终止。这意味着我们不能再信任共享内存的内容了。共享缓冲区内的一些随机块可能会损坏,或者发生了其他可能导致潜在损坏的事情。
2.3 PostgreSQL在kill -9之后实际做了什么?
PostgreSQL处理这种情况的方法是立即删除所有连接,清空内存并从最近的检查点启动WAL恢复。基本上,PostgreSQL做的事情和“正常数据库崩溃”时做的事情是一样的。
其主要优点是您可能会丢失几个数据库连接,并且不会面临数据库损坏。但是,**如果在繁忙的系统上“kill -9”,可能会无意中消除数千个数据库连接,这可能会产生不可预见的后果。因此,首先运行kill -9通常不是一个好主意。如果你这样做,重要的是要了解幕后实际发生了什么,并了解其含义。
以上就是那篇文章的主体内容。其实,我们可以把kill以及signal(信号)发送,整个的知识整理一下。
3. PostgreSQL中用到的信号详解
朋友们,众所周知,PostgreSQL中用到了哪些信号呢?我们可以从停止一个PostgreSQL实例开始探索:
停止PostgreSQL的文档[2]可参考:https://www.postgresql.org/docs/15/app-pg-ctl.html
pg_ctl stop -m i[mmediate] | f[ast] | s[mart]
smart: “智能”模式不允许新连接,然后等待所有现有客户端断开连接。如果服务器处于热备状态,一旦所有客户端断开连接,恢复和流复制将被终止。
fast: “快速”模式(默认)不等待客户端断开连接。将回滚所有活动事务,并强制断开客户端连接,然后关闭服务器。这个是默认停止模式。
immediate: “立即”模式将立即中止所有服务器进程,而不需要彻底关机。这种选择将在下次服务器启动时导致崩溃恢复周期。
3.1 PostgreSQL常见的三个信号
但是实际上,它们是分别往主进程发送三个不同的信号。
1.`SIGTERM`(信号15):这是一个终止信号,通知进程正常关闭。
PostgreSQL 会在接收到此信号后尽快完成当前事务,然后安全地关闭服务器。
这是推荐的关闭 PostgreSQL 服务器的方法。
2.`SIGINT`(信号2):这是一个中断信号,通常用于停止正在运行的程序。
在 PostgreSQL 中,接收到此信号的服务器会尝试中断当前事务并快速关闭。
这可能会导致数据不一致,但在某些情况下(例如,需要紧急关闭服务器以解决问题)
可能是合适的。
3.`SIGQUIT`(信号3):这是一个退出信号,用于立即终止进程。
在 PostgreSQL 中,接收到此信号的服务器会立即关闭,
不等待当前事务完成。这可能导致数据损坏,仅在非常紧急的情况下使用。
具体对应起来:
pg_ctl stop -m smart 相当于 pg_ctl kill TERM {pid} 或者 kill SIGTERM {pid}
pg_ctl stop -m fast 相当于 pg_ctl kill QUIT {pid} 或者 kill SIGQUIT {pid}
pg_ctl stop -m immediate 相当于 pg_ctl kill INT {pid} 或者 kill SIGINT {pid}
而本文开初列出的“-9”只不过是SIGKILL的数值表示。Linux内核实现了约30个信号,每个信号由一个数字标识,介于1到31之间。
PostgreSQL使用了以下这些信号:
ABRT: SIGABRT导致程序异常终止
INT: 外部中断,通常由用户发起。在客户端,它是Control-C的结果,它通常会取消正在运行的程序
KILL: 发送的是SIGKILL信号. 使进程立即终止。KILL 信号不能被捕获或忽略
QUIT: 这是一个更有力的要求。它将不优雅地终止,仍然清理绝对需要清理的资源,但可能不会删除临时文件。当用户按下Ctrl+\时产生这个信号
TERM: SIGTERM信号是用于导致程序终止的通用信号,相当于KILL PID。这用于进程的优雅终止
HUP: 这是一个挂起请求,它用来告诉进程重新初始化自己
USR1: 用户定义信号1
USR2: 用户定义信号2
作为替代操作,我们可以使用pg_cancen_backend(pid)或pg_terminate_backend(pid)指定进程PID,发送相应的信号(分别对应SIGINT和SIGTERM)。
3.2 所有相关信号参考
以下列出所有相关的信号,以供参考:
信 号 | 默认行为 | 描 述 | 信号值 |
---|---|---|---|
SIGABRT | 生成 core 文件然后终止进程 | 这个信号告诉进程终止操作。ABRT 通常由进程本身发送,即当进程调用 abort() 函数发出一个非正常终止信号时 | 6 |
SIGALRM | 终止 | 警告时钟 | 14 |
SIGBUS | 生成 core 文件然后终止进程 | 当进程引起一个总线错误时,BUS 信号将被发送到进程。例如,访问了一部分未定义的内存对象 | 10 |
SIGCHLD | 忽略 | 当了进程结束、被中断或是在被中断之后重新恢复时,CHLD 信号会被发送到进程 | 20 |
SIGCONT | 继续进程 | CONT 信号指不操作系统重新开始先前被 STOP 或 TSTP 暂停的进程 | 19 |
SIGFPE | 生成 core 文件然后终止进程 | 当一个进程执行一个错误的算术运算时,FPE 信号会被发送到进程 | 8 |
SIGHUP | 终止 | 当进程的控制终端关闭时,HUP 信号会被发送到进程 | 1 |
SIGILL | 生成 core 文件然后终止进程 | 当一个进程尝试执行一个非法指令时,ILL 信号会被发送到进程 | 4 |
SIGINT | 终止 | 当用户想要中断进程时,INT 信号被进程的控制终端发送到进程 | 2 |
SIGKILL | 终止 | 发送到进程的 KILL 信号会使进程立即终止。KILL 信号不能被捕获或忽略 | 9 |
SIGPIPE | 终止 | 当一个进程尝试向一个没有连接到其他目标的管道写入时,PIPE 信号会被发送到进程 | 13 |
SIGQUIT | 终止 | 当用户要求进程执行 core dump 时,QUIT 信号由进程的控制终端发送到进程 | 3 |
SIGSEGV | 生成 core 文件然后终止进程 | 当进程生成了一个无效的内存引用时,SEGV 信号会被发送到进程 | 11 |
SIGSTOP | 停止进程 | STOP 信号指示操作系统停止进程的执行 | 17 |
SIGTERM | 终止 | 发送到进程的 TERM 信号用于要求进程终止 | 15 |
SIGTSTP | 停止进程 | TSTP 信号由进程的控制终端发送到进程来要求它立即终止 | 18 |
SIGTTIN | 停止进程 | 后台进程尝试读取时,TTIN 信号会被发送到进程 | 21 |
SIGTTOU | 停止进程 | 后台进程尝试输出时,TTOU 信号会被发送到进程 | 22 |
SIGUSR1 | 终止 | 发送到进程的 USR1 信号用于指示用户定义的条件 | 30 |
SIGUSR2 | 终止 | 同上 | 31 |
SIGPOLL | 终止 | 当一个异步输入/输出时间事件发生时,POLL 信号会被发送到进程 | 23 |
SIGPROF | 终止 | 当仿形计时器过期时,PROF 信号会被发送到进程 | 27 |
SIGSYS | 生成 core 文件然后终止进程 | 发生有错的系统调用时,SYS 信号会被发送到进程 | 12 |
SIGTRAP | 生成 core 文件然后终止进程 | 追踪捕获/断点捕获时,会产生 TRAP 信号。 | 5 |
SIGURG | 忽略 | 当侖一个 socket 有紧急的或是带外数据可被读取时,URG 信号会被发送到进程 | 16 |
SIGVTALRM | 终止 | 当进程使用的虚拟计时器过期时,VTALRM 信号会被发送到进程 | 26 |
SIGXCPU | 终止 | 当进程使用的 CPU 时间超出限制时,XCPU 信号会被发送到进程 | 24 |
SIGXFSZ | 生成 core 文件然后终止进程 | 当文件大小超过限制时,会产生 XFSZ 信号 |
详细参考:
PostgreSQL进程停止时发生了什么, archive进程会完成所有归档吗
参考资料
kill -9 explained for PostgreSQL: https://www.cybertec-postgresql.com/en/kill-9-explained-postgresql/
[2]停止PostgreSQL的文档: https://www.postgresql.org/docs/15/app-pg-ctl.html
往期导读:
1. PostgreSQL中配置单双向SSL连接详解
2. 提升PSQL使用技巧:PostgreSQL中PSQL使用技巧汇集(1)
3. 提升PSQL使用技巧:PostgreSQL中PSQL使用技巧汇集(2)
4. PostgreSQL SQL的基础使用及技巧
5. PostgreSQL开发技术基础:过程与函数
6. PostgreSQL中vacuum 物理文件truncate发生的条件
7. PostgreSQL中表的年龄与Vacuum的实验探索:Vacuum有大用
8. PostgreSQL利用分区表来弥补AutoVacuum的不足
9. 也聊聊PostgreSQL中的空间膨胀与AutoVacuum
10. 正确理解SAP BTP中hyperscaler PG中的IOPS (AWS篇)