PostgreSQL 无法停止的SQL是怎么形成的与 复现这个问题

64349ac0813421297ec032babef907e1.png

在日常的工作中,DB人员会遇到不少的关于停止某个,“有意思” 的SQL的问题,这些SQL 可能正在BLOCKED 掉其他的语句,或者写这个语句的人,正在无意的摧毁整个数据库系统。而DBA只能采用PG的方式 terminal or cancal 这样的语句。

通过两个命令来完成这个任务是PGDB的必须的知识,可如果更细致的问及,内部是怎么进行工作的,这个估计的 take a long story.

Postgresql协议提供了中断运行语句的功能,这个功能是通过打开一个新的连接并且发送一个加密的cancle request  命令来完成的,这个加密的key通过原有的连接产生并转移过来的。主要的原因是如果不这样,每个人都可以cancel你的查询,这样就没有任何的安全可言了。    

libpg 提供了两个函数PQgetcancel () 和 PQcancel 两个函数来获得语句句柄和将其cancel 掉,而在POSTGRESQL中如果你在允许的语句环境中直接使用ctrl + c 会直接启动函数将你的语句被 cancel掉。 

当然postmaster进程接受到你的cancelRequest 的需求,他会发送SIGINT到你的backend所在的数据库的会话,同理如果你触发了 pg_cancel_backend()则也会触发同样的SIGINT 到对应的数据库会话中,而如果你使用了pg_terminate_backend() 函数则会发送信号SIGTERM到对应的会话中。

当进程接受到SIGINT会发送QueryCancelPending 的信号,同理当进程接受到 SIGTERM 的信号会发送ProcDiePending信号,也就是说当系统接受到取消SQL的指令后并不是马上将SQL 停止掉,而是开始设置全局变量来针对这个执行SQL的进程。

设置的主要的原因也很简单,一个SQL在运行中,会改变数据的状态,尤其是内存数据的状态,而改变状态后,将其KILL,则到底内存数据的状态如何变化,这必须被控制和检测,所以设置全局变量的主要思考的地方在于,针对SQL修改的数据是否需要还原进行界定并在系统安全的情况下,才能对SQL所在的进程进行KILL。

CHECK_FOR_INTERRUPTS()宏调用ProcessInterrupts()函数,这些调用散布在PostgreSQL代码中所有安全的地方。然后,该函数将抛出取消当前语句的错误,或者将终止后端进程,这取决于设置了哪个标志。

那么回到主题,为什么你KILL了进程的SQL ,但是却没有效果

1  在执行的过程中,无法调用CHECK_FOR_INTERRUPTS, 并且陷入死循环,在这样的情况下将无法对SQL进行查杀。

2  非POSTGRESQL 中的函数(C 系列)正在被这个函数调用,所以你没有办法调用CHECK_FOR_INTERRUPTS 来终止这个语句。

3  发送的SINGAL 信号卡在系统调用的阶段,这个问题可能来自于操作系统或者硬件系统层级。

我们怎么来模拟这个问题,首先我们建立一个基于C语言的死循环的函数,通过gcc 来编译并且将其加载到POSTGRESQL 系统中的libs 中,注意这里面的权限问题和文件的可执行问题。

然后将这个文件加载到POSTGRESQL 系统中,(因为安全问题,相关的语句和编译方法暂不提供)

下面在POSTGRESQL 系统中直接调用这个C 语言撰写的函数,此时系统在这个backend 进入了死循环,如下图及时在本session中持续不断的按住 ctrl + c 也无济于事。

2359a6e0b80e5e255dec776a72b49937.png

此时,我们看看通过其他的命令是否可以将这个进程或者这个语句杀死。

1 先找到这个语句的PID 

801d061bf4e91327074d7f7a9d09ca36.png

2  然后不断的针对这个PID 进行 pg_terminate_backend 的操作,但可以才能够下图看到,怎么操作,系统均无法响应这个操作。

f986cdf162221fe13740a0918cba17fb.png

然后我们直接针对这个进程,进行DEBUG

181c5e2aa238adcc4ef1e2d072fd0b73.png

977edbc8044dfa31a9adc42abc01b4ba.png

可以从图中发现,主要的原因是调用了/lib64/libc.so.6 的这个系统函数导致的问题。

那么此时如何将这个进程KILL 掉,别无他法,只能在数据库外部对这个进程进行KILL -9 的操作。当然这个操作是具有危险性的,所以必须注意相关的操作的时间和相关数据库应该在操作前进行备份,甚至停止业务。

而这个问题根本的解决方案在于在外部函数调用的过程中,必须在编写外部函数时在循环中加入对于 CHECK_FOR_INTERRUPTS()函数的调用,来解决这个问题。

所以此时你还敢随便使用没有被经过验证的外部C语言函数在POSTGRESQL 内部的使用,以及一些来路不明的 extension 吗?

be22567afccaa7740ed78c21f2fe02be.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用INSERT INTO SELECT语句来实现将一个表的数据插入到另一个表中。例如: ``` INSERT INTO table2 (col1, col2, col3) SELECT col1, col2, col3 FROM table1; ``` 这条SQL语句将会把table1中的col1、col2、col3列的数据插入到table2中的对应列中。 ### 回答2: 在PostgreSQL中,可以使用多种方式来一次性写入两个表。 一种方式是使用WITH子句或者临时表来将数据从一个表复制到另一个表。例如,假设有两个表table1和table2,它们具有相同的结构,我们可以使用以下的SQL语句将table1中的数据一次性写入table2: WITH temp_table AS ( SELECT * FROM table1 ) INSERT INTO table2 SELECT * FROM temp_table; 另一种方式是使用INSERT INTO ... SELECT语句来将数据一次性写入多个表。假设有两个表table1和table2,我们可以使用以下的SQL语句将table1中的数据一次性写入table2和另外一个表table3: INSERT INTO table2 (column1, column2, ...) SELECT column1, column2, ... FROM table1; INSERT INTO table3 (column1, column2, ...) SELECT column1, column2, ... FROM table1; 在以上的语句中,column1、column2等表示要插入的列名称,需要根据表的结构进行调整。 总结起来,通过使用WITH子句或者临时表,或者使用INSERT INTO ... SELECT语句,我们可以将数据一次性写入到多个表中。具体的方法取决于具体的需求和表的结构。 ### 回答3: 在PostgreSQL中,可以使用JOIN语句将两个表合并到一条SQL查询中。 例如,假设我们有两个表:表A和表B,它们之间有一个共同的列(例如,列"ID")。 要在一条SQL语句中使用这两个表,可以使用INNER JOIN语句来合并它们。下面是一个例子: SELECT * FROM tableA INNER JOIN tableB ON tableA.ID = tableB.ID; 这条SQL语句以表A为主表,并使用INNER JOIN连接表B。连接条件是根据ID列相等来进行匹配。使用"*"代替SELECT语句中的列名表示选择所有列。 当执行此SQL查询时,它将返回两个表中具有相同ID值的行的组合。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值