最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

掛起的PostgreSQL查詢?nèi)∠椒?/h1>
 更新時間:2026年05月07日 08:49:28   作者:北重樓01  
本文介紹了在PostgreSQL中取消卡住查詢的方法,當常規(guī)取消方法無效時,可通過gdb調(diào)試器直接向卡住的后臺進程發(fā)送ProcessInterrupts()信號,使其安全退出,此操作需謹慎評估風險,需要的朋友可以參考下

有時,PostgreSQL 中的某個查詢會運行異常緩慢,甚至陷入“掛起”狀態(tài)。通常情況下,我們可以輕松地將其中斷(取消)。但有時這會變成一個棘手的問題:查詢無法被取消。本文將探討可能導致這一問題的原因,并介紹一個解決方案的技巧(該操作風險較高,請謹慎評估!

如何取消正在運行的查詢

PostgreSQL 協(xié)議中規(guī)定了中斷正在執(zhí)行的語句的方法:建立一個新的連接,并發(fā)送一個包含秘鑰CancelRequest 消息。該秘鑰在原始連接建立時由服務器發(fā)送。沒有這個秘鑰,任何人都可以取消您的查詢,這將帶來嚴重的安全隱患。

C 語言庫 libpq 提供了 PQgetCancel()PQcancel() 函數(shù)來取消查詢,其他數(shù)據(jù)庫 API 通常也有類似的功能。在交互式 psql 會話中,您只需按下 Ctrl+C 即可發(fā)送取消請求,圖形化客戶端也通常會提供一個用于此目的的按鈕。

此外,也可以通過調(diào)用數(shù)據(jù)庫函數(shù) pg_cancel_backend() 來取消他人的查詢。另一個選項是 pg_terminate_backend(),其作用更進一步——直接終止他人的數(shù)據(jù)庫會話。要使用這些函數(shù),您必須是超級用戶,或者是默認角色 pg_signal_backend 的成員(原因?qū)⒃谙乱还?jié)解釋),或者您必須使用與目標會話相同的數(shù)據(jù)庫用戶登錄(即您有權(quán)取消自己的語句)。

服務器如何處理取消請求

PostgreSQL 中的進程間通信高度依賴信號機制。

postmaster 進程收到 CancelRequest 消息時,它會向?qū)獢?shù)據(jù)庫會話的后臺進程發(fā)送 SIGINT 信號。pg_cancel_backend() 函數(shù)執(zhí)行的也是此操作。而 pg_terminate_backend() 發(fā)送的是 SIGTERM 信號。

每個 PostgreSQL 進程都設(shè)有一個信號處理器,用于處理接收到的信號。該處理器不會立即中斷后臺進程,而是為進程設(shè)置全局標志位SIGINT 設(shè)置 QueryCancelPending,SIGTERM 設(shè)置 ProcDiePending。后臺進程有責任在合適的時機檢查這些標志位并做出響應。這種設(shè)計確保了進程不會在不安全的狀態(tài)(例如,正在修改共享內(nèi)存時)被意外中斷。

在 PostgreSQL 代碼中的安全位置,遍布著 CHECK_FOR_INTERRUPTS() 宏(該宏會調(diào)用 ProcessInterrupts() 函數(shù))。該函數(shù)會根據(jù)設(shè)置的標志,拋出錯誤以取消當前語句,或終止后臺進程。

取消查詢可能失敗的原因

查詢?nèi)∠】赡苡幸韵聨追N原因:

  • 執(zhí)行卡在了一個不包含 CHECK_FOR_INTERRUPTS() 的循環(huán)中:這屬于 PostgreSQL 自身的缺陷,修復方法是增加對該宏的調(diào)用。
  • 執(zhí)行卡在了一個通過 SQL 語句調(diào)用的第三方 C 函數(shù)里:請將此問題報告給該函數(shù)的作者。
  • 執(zhí)行卡在了一個無法中斷的系統(tǒng)調(diào)用中:這通常表明操作系統(tǒng)或硬件層面存在問題。需要注意的是,當進程處于內(nèi)核空間時,信號的傳遞會被延遲。

謹慎使用kill -9

直接使用普通的 kill 命令處理 PostgreSQL 后臺進程是可行的。這會發(fā)送 SIGTERM 信號,效果等同于為后臺進程調(diào)用 pg_terminate_backend()。如果此操作無效,有人可能會嘗試使用 kill -9,它會發(fā)送 SIGKILL 信號。該信號無法被捕獲,會立即終止進程。問題在于,postmaster 會檢測其子進程是否正常退出。一旦檢測到異常終止,它會殺死所有其他 PostgreSQL 進程并執(zhí)行崩潰恢復,這將導致整個數(shù)據(jù)庫短暫停服,持續(xù)時間從數(shù)秒到數(shù)分鐘不等。

請注意,對后臺進程使用 kill -9 僅會造成短暫停機,但對 postmaster 進程本身使用 kill -9 則會產(chǎn)生更嚴重的后果,應堅決避免。這會打開一個時間窗口,在此期間新的 postmaster 可能被啟動,而舊 postmaster 的某些子進程仍然存活,極有可能導致磁盤上的數(shù)據(jù)損壞。永遠不要使用 kill -9 殺死 postmaster 進程!

有時,即使是 kill -9 也無法殺死一個 PostgreSQL 后臺進程。這意味著該進程卡在了一個不可中斷的系統(tǒng)調(diào)用中,例如,在對已不可用的網(wǎng)絡附加存儲執(zhí)行 I/O 操作。如果這種狀況持續(xù)存在,唯一的方法就是重啟操作系統(tǒng)。

高級技巧:在不重啟服務器的情況下取消卡住的查詢

有時,您可以按照以下步驟操作,以避免數(shù)據(jù)庫崩潰恢復和停服。以下示例基于 Linux 環(huán)境下的 GNU 調(diào)試器 gdb;其他環(huán)境請自行適配。

1. 創(chuàng)建一個會掛起的函數(shù)示例

我們編寫一個簡單的 C 函數(shù)(源文件 loop.c):

#include "postgres.h"
#include "fmgr.h"
#include <unistd.h>
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(loop);
Datum
loop(PG_FUNCTION_ARGS)
{
    /* 一個無限循環(huán) */
    while(1)
        sleep(2);
}

按如下方式構(gòu)建共享庫(請根據(jù)實際環(huán)境調(diào)整包含路徑):

gcc -I /usr/pgsql-14/include/server -fPIC -shared -o loop.so loop.c

將生成的 loop.so 文件復制到 PostgreSQL 的共享庫目錄(可通過 pg_config --libdir 獲?。?。

2. 定義并調(diào)用函數(shù)

以超級用戶身份在 SQL 中定義函數(shù):

CREATE FUNCTION loop() RETURNS void
LANGUAGE c AS 'loop';

然后,以普通用戶身份調(diào)用該函數(shù):

SELECT loop();

執(zhí)行將陷入掛起。您可以嘗試取消查詢,但它會持續(xù)運行。

3. 定位掛起的后臺進程并發(fā)送終止信號

使用相同的數(shù)據(jù)庫用戶打開另一個數(shù)據(jù)庫連接,通過以下查詢找出該會話的后臺進程 ID:

SELECT pid, query
FROM pg_stat_activity
WHERE query LIKE '%loop%';

獲取進程 ID 后,向其發(fā)送 SIGTERM 信號:

SELECT pg_terminate_backend(12345);

(請將 12345 替換為實際進程 ID)。該函數(shù)返回 TRUE,表示信號已發(fā)送,但查詢?nèi)栽趫?zhí)行。

4. 用調(diào)試器附加到進程

確保已安裝 gdb 調(diào)試器。為獲得可讀的堆棧跟蹤,建議安裝 PostgreSQL 服務器的調(diào)試符號(但這并非本技巧的必需步驟)。以 PostgreSQL 用戶(通常名為 postgres)身份登錄到數(shù)據(jù)庫服務器,按如下方式調(diào)用 gdb(請使用正確的 postgres 可執(zhí)行文件路徑和進程 ID):

gdb /usr/pgsql-14/bin/postgres 12345

(gdb) 提示符下,輸入 bt 命令生成堆棧跟蹤,輸出類似如下:

#0  __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0,
    req=req@entry=0x7ffdaf61cde0, rem=rem@entry=0x7ffdaf61cde0)
    at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:71
#1  0x00007f113d864897 in __GI___nanosleep (req=req@entry=0x7ffdaf61cde0,
    rem=rem@entry=0x7ffdaf61cde0) at ../sysdeps/unix/sysv/linux/nanosleep.c:25
#2  0x00007f113d8647ce in __sleep (seconds=0) at ../sysdeps/posix/sleep.c:55
#3  0x00007f113e623139 in loop () from /usr/pgsql-14/lib/loop.so
#4  0x00000000006d71fb in ExecInterpExpr (state=0x13837b8, econtext=0x13834e0,
    isnull=<optimized out>) at executor/execExprInterp.c:1260
#5  0x000000000070e391 in ExecEvalExprSwitchContext (isNull=0x7ffdaf61ced7,
    econtext=0x13834e0, state=0x13837b8)
    at executor/../../../src/include/executor/executor.h:339
#6  ExecProject (projInfo=0x13837b0)
    at executor/../../../src/include/executor/executor.h:373
#7  ExecResult (pstate=<optimized out>) at executor/nodeResult.c:136
#8  0x00000000006da8b2 in ExecProcNode (node=0x13833d0)
    at executor/../../../src/include/executor/executor.h:257
#9  ExecutePlan (execute_once=<optimized out>, dest=0x137f4c0, direction=<optimized out>,
    numberTuples=0, sendTuples=<optimized out>, operation=CMD_SELECT,
    use_parallel_mode=<optimized out>, planstate=0x13833d0, estate=0x13831a8)
    at executor/execMain.c:1551
[...]

堆棧跟蹤有助于定位問題根源。如果您需要向 PostgreSQL 報告此問題,請附上此信息。

如果您不想繼續(xù)下一步,可以在 (gdb) 提示符下輸入 detach 分離調(diào)試器,讓進程繼續(xù)運行。

5. 通過讓卡住的后臺進程干凈退出以取消執(zhí)行

觀察上面的堆棧跟蹤,可以看到當前執(zhí)行點位于一個自定義函數(shù)中(loop () from /usr/pgsql-14/lib/loop.so),而不是 PostgreSQL 內(nèi)部代碼。這種情況下,讓進程退出是相對安全的。如果執(zhí)行點位于 PostgreSQL 服務器內(nèi)部,則可能存在一定的風險(例如,進程可能正持有自旋鎖或處于修改共享狀態(tài))。若您熟悉 PostgreSQL 源碼,可以通過調(diào)用棧評估風險。

現(xiàn)在,如果決定繼續(xù),請在 (gdb) 提示符下調(diào)用 ProcessInterrupts() 函數(shù)。由于此前已設(shè)置了 ProcDiePending 標志,該調(diào)用將導致進程退出:

(gdb) print ProcessInterrupts()
[Inferior 1 (process 12345) 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.
(gdb) quit

6. 改進函數(shù)以支持取消

為了避免此類問題,函數(shù)代碼應修改為定期檢查中斷標志:

#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)
{
    /* 一個無限循環(huán),但允許中斷 */
    while(1)
    {
        CHECK_FOR_INTERRUPTS();
        sleep(2);
    }
}

修改后,函數(shù)將每兩秒檢查一次中斷標志,從而能夠被安全地取消。

結(jié)論

取消 PostgreSQL 查詢的本質(zhì)是向后臺進程發(fā)送 SIGINT 信號。如果 SIGINTSIGTERM 均無法中斷進程,您可以使用 gdb 附加到該進程,并直接調(diào)用 ProcessInterrupts() 函數(shù),使其干凈退出。

以上就是掛起的PostgreSQL查詢?nèi)∠椒ǖ脑敿殐?nèi)容,更多關(guān)于取消掛起PostgreSQL查詢的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • CVE-2019-9193之PostgreSQL?任意命令執(zhí)行漏洞的問題

    CVE-2019-9193之PostgreSQL?任意命令執(zhí)行漏洞的問題

    這篇文章主要介紹了CVE-2019-9193:PostgreSQL?任意命令執(zhí)行漏洞,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • 最新評論

    南宫市| 自治县| 蓬溪县| 会昌县| 伊宁县| 大庆市| 依兰县| 仁化县| 将乐县| 青铜峡市| 蕉岭县| 武强县| 通州市| 临湘市| 社会| 连云港市| 永福县| 保亭| 牙克石市| 赤水市| 乃东县| 德庆县| 措美县| 上林县| 湘乡市| 昂仁县| 泰州市| 芜湖市| 建德市| 萨迦县| 商水县| 东至县| 铁岭市| 武山县| 雷州市| 长治县| 元阳县| 上蔡县| 遵化市| 彭阳县| 肇州县|