You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL15中DROP TABLESPACE引发ProcSignalBarrier死锁问题咨询

问题描述

执行了以下操作:

  1. 创建表空间:
create tablespace test_tablespace location '/path/to/tablespace/dir';
  1. 创建表并插入数据:
create table test_table (id numeric, file_name varchar) tablespace test_tablespace;

insert into test_table (id, file_name) values (1,'test_field1'),(2,'test_field2'),(3,'test_field3');
  1. 创建使用dblink的函数来删除表和表空间并执行:
CREATE OR REPLACE FUNCTION test_func(par_tablespace_name varchar)
    RETURNS void
    LANGUAGE 'plpgsql'
AS $BODY$
begin
  perform dblink_connect_u('port=5432 user=postgres dbname=test_db');
  perform dblink_exec('drop table test_table');
  perform dblink_exec('drop tablespace '|| par_tablespace_name );
  perform dblink_disconnect();
end;
$BODY$;

select test_func('test_tablespace');

执行后触发死锁,相关信息如下:

pg_stat_activity 信息

pidwait_event_typewait_eventquery
609024nullnullselect test_func('test_tablespace');
610910IPCProcSignalBarrierdrop tablespace test_tablespace

pg_blocking_pids(610910)结果为null。

PostgreSQL 日志

[610910] STATEMENT:  drop tablespace test_tablespace
[610910] LOG:  still waiting for backend with PID 609024 to accept ProcSignalBarrier

该操作在PostgreSQL13中可正常执行,且当表空间为空(无数据文件)时也无问题。

现咨询:该死锁产生的原因是什么?如何通过其他会话的函数删除表空间?


死锁原因分析
  • 执行DROP TABLESPACE时,PostgreSQL需要通知所有曾经访问过该表空间的后端进程(包括创建过该表空间内表的会话),通过ProcSignalBarrier机制同步状态,确保这些会话不再持有该表空间的相关资源。
  • 主会话(PID 609024)正是创建test_table的会话,属于需要被通知的对象,但此时主会话正处于等待dblink会话(PID 610910)完成DROP TABLESPACE的状态,无法响应信号。
  • dblink会话则一直在等待主会话响应信号,形成循环等待,最终触发死锁。
  • 空表空间时,没有关联的使用记录,无需通知其他会话;PostgreSQL13对DROP TABLESPACE的信号处理逻辑与当前版本不同,因此未触发该问题。

解决方法

要通过函数删除表空间,核心是打破循环等待的条件,让执行DROP TABLESPACE的会话无需等待当前调用函数的主会话,或提前释放主会话的关联资源:

1. 拆分dblink连接,断开主会话关联

将删除表和删除表空间的操作拆分为两次独立的dblink连接,让主会话在执行DROP TABLESPACE前释放与表空间的关联:

CREATE OR REPLACE FUNCTION test_func(par_tablespace_name varchar)
    RETURNS void
    LANGUAGE 'plpgsql'
AS $BODY$
begin
  -- 第一次连接:删除表空间内的表
  perform dblink_connect_u('port=5432 user=postgres dbname=test_db');
  perform dblink_exec('drop table test_table');
  perform dblink_disconnect();
  
  -- 短暂等待,确保资源完全释放
  perform pg_sleep(1);
  
  -- 第二次连接:删除表空间(此时主会话无表空间关联资源)
  perform dblink_connect_u('port=5432 user=postgres dbname=test_db');
  perform dblink_exec('drop tablespace '|| par_tablespace_name );
  perform dblink_disconnect();
end;
$BODY$;

2. 提前结束主会话事务

在调用删除函数前,先执行COMMIT结束当前主会话的事务,确保主会话不再持有任何与目标表空间相关的资源,再执行select test_func('test_tablespace');。

3. 使用全新会话执行删除操作

直接在未访问过该表空间的全新会话中执行删除函数,或单独执行DROP TABLESPACE(前提是已删除表空间内所有对象)。


内容的提问来源于stack exchange,提问作者MegaNeiv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 23:47:37