在使用数据库过程中,我们难免要终止一些正在执行的查询等语句,比如不合理的超长大事物,对数据库性能有影响的偶发性查询。一般在pg中使用以下两个函数终止相关查询,这里不在详述两个函数的区别。

直接给出官方文档解释:

pg_cancel_backend 调用系统信号 SIGINT 对应信号2
pg_terminate_backend 调用系统信号 SIGTERM 对应信号15

--定义在/usr/include/asm/signal.h
#define SIGHUP           1
#define SIGINT           2
#define SIGQUIT          3
#define SIGILL           4
#define SIGTRAP          5
#define SIGABRT          6
#define SIGIOT           6
#define SIGBUS           7
#define SIGFPE           8
#define SIGKILL          9
#define SIGUSR1         10
#define SIGSEGV         11
#define SIGUSR2         12
#define SIGPIPE         13
#define SIGALRM         14
#define SIGTERM         15
#define SIGSTKFLT       16
#define SIGCHLD         17
#define SIGCONT         18
#define SIGSTOP         19
#define SIGTSTP         20
#define SIGTTIN         21
#define SIGTTOU         22
#define SIGURG          23
#define SIGXCPU         24
#define SIGXFSZ         25
#define SIGVTALRM       26
#define SIGPROF         27
#define SIGWINCH        28
#define SIGIO           29
#define SIGPOLL         SIGIO
/*
#define SIGLOST         29
*/
#define SIGPWR          30
#define SIGSYS          31
#define SIGUNUSED       31

或者kill -l也可以查看

没安装man-pages的话,执行man 7 signal会报错No manual entry for signal in section 7
安装man-pages

yum install -y man-pages

看下POSIX.1-1990标准中描述的信号

postgresql的pg_ctl可以通过kill对相应进程发送以下几种信号

pg_ctl kill SIGNALNAME pid
SIGNALNAME可以是 ABRT HUP INT KILL QUIT TERM USR1 USR2中的一个

举例:
查找到postgres主进程号为4209

发送INT信号给4209进行


有了以上的介绍,我们描述下postgresql中是如何取消查询等语句的,PostgreSQL协议对中断正在运行的语句是有一定准则的。它通过打开一个新连接并发送一个带有密钥的CancelRequest消息来完成的。在初始连接开始期间,服务端会发送该密钥。如果没有这个密钥,每个人都可以取消并终止我们的查询,这是一个不可接受的安全问题。

C库中的libpq提供了PQgetCancel()和PQcancel()函数来取消查询,其他数据库api也应该有类似的函数。在交互式psql会话中,我们可以简单地按Ctrl+C发送取消请求,这个操作我们应该会经常用到,GUI客户端通常有一个用于取消终止的按钮。

数据库服务如何处理取消请求

PostgreSQL进程间通信很大程度上依赖于信号。
postmaster进程接收到CancelRequest时,它向相应数据库会话的后端进程发送SIGINT信号。该信号是由函数pg_cancel_backend()发送。pg_terminate_backend()发送信号SIGTERM。文章最开始有说明。

每个PostgreSQL进程都有一个信号处理器,当接收到信号时,会对这些信号进行处理。这个信号处理器不会立即中断后端进程,但它会为该进程设置全局变量。SIGINT将设置QueryCancelPending, SIGTERM将设置ProcDiePending。这些变量作为标志,并在适当的时候,由后端进程负责对它们做出反应。这确保了进程不会在不适宜的时候被中断,例如,当该进程使共享内存处于不一致的状态的时候。

通过CHECK_FOR_INTERRUPTS()宏调用ProcessInterrupts()函数,这些调用分布在PostgreSQL代码中很多地方。然后,该函数将抛出是取消当前语句的错误,还是终止后端进程的错误,这取决于之前设置的变量标志(SIGINT将设置QueryCancelPending, SIGTERM将设置ProcDiePending)。

CHECK_FOR_INTERRUPTS宏定义如下:

/* Service interrupt, if one is pending and it's safe to service it now */
#define CHECK_FOR_INTERRUPTS() \
do { \if (INTERRUPTS_PENDING_CONDITION()) \ProcessInterrupts(); \
} while(0)

发出终止或者取消的命令后,有时候为什么没效果呢?

  1. 发出的命令被卡在不包含CHECK_FOR_INTERRUPTS()的循环中。
  2. 在SQL语句中调用的第三方C函数的执行被卡住了。在这种情况下,可以将错误报告给函数的作者。
  3. 在无法中断的系统调用中被卡住。这可能是操作系统或硬件层面的问题。注意,当进程处于内核空间中时,信号的传递会被延迟。
如果终止或取消的命令没效果,有些人可能会使用kill -9 pid,那么在postgresql中这会带来什么后果呢?

在PostgreSQL后台进程上使用普通的kill是完全没问题的。这将发送SIGTERM信号,它与调用pg_terminate_backend()是相同的。如果这都没有效果,那么很容易使用kill -9,它会发送SIGKILL。该信号无法被捕获并立即终止进程。问题是postmaster如果探测到一个子进程没有干净地关闭。那么,它将杀死所有其他PostgreSQL进程,并进行崩溃恢复,这将导致整个数据库中断,可能需要几秒到几分钟的时间。如果数据库很大并且很繁忙的话,可能需要更久。

注意,虽然在普通的后端进程使用kill -9会导致短时间的停机,但在postmaster进程上使用kill -9会造成更恶劣的影响,因此要避免在postmaster上使用kill -9。它打开了一个时间窗口,在此期间可以启动一个新的postmaster,而旧的postmaster的一些子进程还存活着,这很可能导致磁盘上的数据损坏。所以,永远,永远不要用kill -9杀死postmaster进程!

有时候,kill -9也不能杀死PostgreSQL后台进程。这意味着后端被卡在一个不可中断的系统调用中,例如在不再可用的网络存储上有I/O操作。如果这种情况一直持续,那么摆脱该进程的唯一方法就是重启操作系统。

下面我们模拟一个无法终止查询的例子:

创建一个c函数:

--loop.c文件内容如下 #include "postgres.h"
#include "fmgr.h"#include <unistd.h>PG_MODULE_MAGIC;PG_FUNCTION_INFO_V1(loop);Datum loop(PG_FUNCTION_ARGS)
{/* 一个无限循环 */while(1)sleep(2);
}-- 创建共享库文件gcc -I /opt/pgsql13/include/server/ -fPIC -shared -o loop.so loop.c-- 查看pg的libdir,拷贝共享库文件到该目录
pg_config --libdir
/opt/pgsql13/lib
mv loop.so  /opt/pgsql13/lib

进入数据库,使用共享库文件创建loop函数,然后调用

CREATE  OR REPLACE FUNCTION loop() RETURNS void as
'loop.so', 'loop'LANGUAGE c ;#执行该函数
select loop()#使用任意方式终止取消该查询
#在psql下ctrl+c无用,如下
postgres=# select loop();
^CCancel request sent#另外开启一个session,查询pg_stat_activity,如下
postgres=# SELECT pid, query FROM pg_stat_activity WHERE query LIKE '%loop%';pid  |                               query
-------+--------------------------------------------------------------------31449 | select loop();31853 | SELECT pid, query FROM pg_stat_activity WHERE query LIKE '%loop%';#使用终止函数,一样无效
postgres=# SELECT pg_terminate_backend(31449);pg_terminate_backend
----------------------t
(1 row)#该函数还在执行
postgres=# SELECT pid, query FROM pg_stat_activity WHERE query LIKE '%loop%';pid  |                               query
-------+--------------------------------------------------------------------31449 | select loop();31853 | SELECT pid, query FROM pg_stat_activity WHERE query LIKE '%loop%';

通过gdb调试一下

gdb /opt/pgsql13/bin/postgres 31449#生成堆栈跟踪信息
(gdb) bt
#0  0x00007ff0535c58d0 in __nanosleep_nocancel () from /lib64/libc.so.6
#1  0x00007ff0535c5784 in sleep () from /lib64/libc.so.6
#2  0x00007ff03509f725 in loop () from /opt/pgsql13/lib/loop.so
#3  0x0000000000619db2 in ExecEvalFuncExprFusage (state=state@entry=0x15127f8, op=op@entry=0x1512a08, econtext=econtext@entry=0x1512520)at execExprInterp.c:2341
#4  0x000000000061c88e in ExecInterpExpr (state=0x15127f8, econtext=0x1512520, isnull=<optimized out>) at execExprInterp.c:715
#5  0x000000000064c5ff in ExecEvalExprSwitchContext (isNull=0x7ffc3a795f6f, econtext=0x1512520, state=0x15127f8)at ../../../src/include/executor/executor.h:322
#6  ExecProject (projInfo=0x15127f0) at ../../../src/include/executor/executor.h:356
#7  ExecResult (pstate=<optimized out>) at nodeResult.c:136
#8  0x0000000000620d12 in ExecProcNode (node=0x1512410) at ../../../src/include/executor/executor.h:248
#9  ExecutePlan (execute_once=<optimized out>, dest=0x1514838, direction=<optimized out>, numberTuples=0, sendTuples=true, operation=CMD_SELECT, use_parallel_mode=<optimized out>, planstate=0x1512410, estate=0x15121e8) at execMain.c:1632
#10 standard_ExecutorRun (queryDesc=0x14f88e8, direction=<optimized out>, count=0, execute_once=<optimized out>) at execMain.c:350
#11 0x000000000077b25b in PortalRunSelect (portal=portal@entry=0x1498c08, forward=forward@entry=true, count=0, count@entry=9223372036854775807, dest=dest@entry=0x1514838) at pquery.c:921
#12 0x000000000077c678 in PortalRun (portal=portal@entry=0x1498c08, count=count@entry=9223372036854775807, isTopLevel=isTopLevel@entry=true, run_once=run_once@entry=true, dest=dest@entry=0x1514838, altdest=altdest@entry=0x1514838, qc=qc@entry=0x7ffc3a7961e0) at pquery.c:765
#13 0x000000000077835e in exec_simple_query (query_string=0x13fcf78 "select loop();") at postgres.c:1239
#14 0x00000000007796d2 in PostgresMain (argc=<optimized out>, argv=argv@entry=0x1427a78, dbname=0x14279b8 "postgres", username=<optimized out>)at postgres.c:4337
#15 0x000000000048650c in BackendRun (port=<optimized out>, port=<optimized out>) at postmaster.c:4550
#16 BackendStartup (port=0x141f650) at postmaster.c:4234
#17 ServerLoop () at postmaster.c:1739
#18 0x0000000000706b88 in PostmasterMain (argc=argc@entry=3, argv=argv@entry=0x13f7b90) at postmaster.c:1412
#19 0x000000000048718a in main (argc=3, argv=0x13f7b90) at main.c:210#由以上堆栈我们看到loop () from /opt/pgsql13/lib/loop.so和sleep () from /lib64/libc.so.6,
#而且也设置了ProcDiePending,所以我们只需要调用ProcessInterrupts()即可退出正在执行的loop。
(gdb)  print ProcessInterrupts()
[Inferior 1 (process 31449) exited with code 01]
The program being debugged exited while in a function called from GDB.
Evaluation of the expression containing the function
(ProcessInterrupts) will be abandoned.

再次查看原来的session:

#已被终止,并重置了session
postgres=# select loop();
^CCancel request sentFATAL:  terminating connection due to administrator command
server closed the connection unexpectedlyThis probably means the server terminated abnormallybefore or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.#查看正在执行的语句已经消失不在
postgres=# SELECT pid, query FROM pg_stat_activity WHERE query LIKE '%loop%';pid  |                               query
-------+--------------------------------------------------------------------31853 | SELECT pid, query FROM pg_stat_activity WHERE query LIKE '%loop%';
修改代码,做一个可以中断取消的例子:
-- loop_01.c内容如下
#include "postgres.h"
#include "fmgr.h"
#include "miscadmin.h"#include <unistd.h>PG_MODULE_MAGIC;PG_FUNCTION_INFO_V1(loop);Datum loop(PG_FUNCTION_ARGS)
{/* 加入CHECK_FOR_INTERRUPTS,每两秒检查一次中断,以便可以手动终止 */while(1){CHECK_FOR_INTERRUPTS();sleep(2);}
}
-- 方便起见,共享库文件还是用loop.so
gcc -I /opt/pgsql13/include/server/ -fPIC -shared -o loop.so loop_01.cmv /home/postgres/loop.so /opt/pgsql13/lib/--psql中可以取消终止
postgres=# select loop();
^CCancel request sent
ERROR:  canceling statement due to user request--pg_terminate_backend也可以正常终止
postgres=#   SELECT pid, query FROM pg_stat_activity WHERE query LIKE '%loop%';pid  |                               query
-------+--------------------------------------------------------------------322 | select loop();31853 | SELECT pid, query FROM pg_stat_activity WHERE query LIKE '%loop%';
(2 rows)postgres=#  SELECT pg_terminate_backend(322);pg_terminate_backend
----------------------t
(1 row)postgres=#   SELECT pid, query FROM pg_stat_activity WHERE query LIKE '%loop%';pid  |                               query
-------+--------------------------------------------------------------------31853 | SELECT pid, query FROM pg_stat_activity WHERE query LIKE '%loop%';
(1 row)

通过以上实例, 我们知道取消查询就是向后端发送一个SIGINT信号。如果SIGINT和SIGTERM都不能中断后端进程,我们可以用gdb连接到挂起的后端进程,并直接调用ProcessInterrupts()使其退出。

参考:
https://www.postgresql.org/docs/current/protocol-flow.html#id-1.10.5.7.9
https://postgreshelp.com/operating-system-kill-signals-on-postgresql/
https://www.cybertec-postgresql.com/en/cancel-hanging-postgresql-query/

Postgresql中如何终止正在执行的查询相关推荐

  1. postgresql学习_在PostgreSQL中学习这些快速技巧

    postgresql学习 PostgreSQL is one of the most popular open source SQL dialects. One of its main advanta ...

  2. PostgreSQL中的查询:1.查询执行阶段

    PostgreSQL中的查询:1.查询执行阶段 开始关于PG内部执行机制的文章系列.这一篇侧重于查询计划和执行机制. 本系列包括: 1.查询执行阶段(本文) 2.统计数据 3.顺序扫描 4.索引扫描 ...

  3. PostgreSQL中的执行计划

    PostgreSQL中的执行计划 EXPLAN 预生成执行计划 EXPLAN sql 真实执行计划 explan analyze sql 输出详细内容 explan(analyze on, timin ...

  4. 一条 Select 语句 在 Postgresql 中的执行链路

    本文只是逻辑上的概览,并没有太过深入的设计细节的描述,对应的postgresql 的代码版本是 REL_12_2 1. DML 语句入口 如何搭建 gdb 调试pg 源代码环境,可以参考:利用GDB ...

  5. android 定时器5秒执行一次,如何在android中每30秒执行一次查询?

    我有一个查询,我想每30秒执行一次并将其记录到Logcat.我是通过处理程序完成的,我没有得到回应.如何在android中每30秒执行一次查询? 这里是我的代码: runnable = new Run ...

  6. oracle执行脚本顺序执行吗,【ORACLE】记录通过执行Oracle的执行计划查询SQL脚本中的效率问题 - 不及格的飞鱼...

    记录通过执行Oracle的执行计划查询SQL脚本中的效率问题 问题现象: STARiBOSS5.8.1R2版本中,河北对帐JOB执行时,无法生成发票对帐文件. 首先,Quartz表达式培植的启动时间为 ...

  7. 在VS Code中执行SQL查询,是怎样一种体验?

    上次,我们演示了"如何使用Nuget包XPlot.Plotly.Interactive在.NET Interactive notebook中绘制图表". 这次,我们使用Nuget包 ...

  8. activiti自定义_在Activiti中执行自定义查询

    activiti自定义 (这可能最终会出现在Activiti 5.15版本的用户指南中,但是我已经想要共享它了) Activiti API允许使用高级API与数据库进行交互. 例如,对于检索数据,查询 ...

  9. 在Activiti中执行自定义查询

    (这可能最终会出现在Activiti 5.15版本的用户指南中,但是我已经想共享它了) Activiti API允许使用高级API与数据库进行交互. 例如,对于检索数据,查询API和本机查询API的用 ...

最新文章

  1. TensorFlow高阶 API: keras教程-使用tf.keras搭建mnist手写数字识别网络
  2. Android—打包aar以及module依赖操作
  3. angular——更多按钮的上拉菜单(路由跳转)
  4. python并行for循环_Python并行执行for循环
  5. 如何在OpenJDK中使用ECC
  6. 安装nodejs插件并在sublime text 3上使用
  7. NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
  8. Atitit.ati dwr的原理and设计 attilax 总结 java php 版本
  9. 如何关闭极域课堂(亲测有效)(含下载链接)
  10. zend studio php调试,Zend Studio中如何配置和使用xdebug断点调试工具?
  11. 计算计算机系统包括哪些内容,什么是MIPS计算机系统的运算器
  12. OIO下的socket传输文件
  13. Web:flex模拟移动商城首页页面布局/grid布局的相关属性
  14. 源码安装 apache 2018-09-13
  15. Java正则表达式的语法与示例
  16. Facebook的预填问题默认可以设定哪些类型。
  17. linux系统安装搜狗输入法
  18. 安卓更新下载apk 并安装
  19. 264编码 yocto_评测
  20. 双目测距+点云——使用MiddleBurry数据集的图片

热门文章

  1. 制作京东快报页面html,HTML第6章上机练习3(制作京东快报页面)
  2. 2022年A特种设备相关管理(电梯)考试题模拟考试平台操作
  3. 复现 MonoEF:Monocular 3D Object Detection: An Extrinsic Parameter Free Approach
  4. Qt解决中文显示乱码问题
  5. U盘版的DOS启动盘制作
  6. 电子书籍下载网站集锦(不断更新中...)
  7. 计算机等级考试二级ppt,高校计算机等级考试二级C.ppt
  8. M1卡的简介与操作命令
  9. 【正点原子MP157连载】第二十章 字符设备驱动开发-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7
  10. 4G LTE频带划分和国内运行商资源分配