存储过程本身死锁

dez*_*zso 5 postgresql trigger deadlock postgresql-9.2

我有一个奇怪的情况,从日志中看到:

Process 37278 waits for ExclusiveLock on advisory lock [16421,999999,12864385,2]; blocked by process 53807. 
Process 53807 waits for ExclusiveLock on advisory lock [16421,999999,12864395,2]; blocked by process 37278. 
Process 37278: SELECT * FROM zel_api.insert_event ($1 ,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24) 
Process 53807: SELECT * FROM zel_api.insert_event ($1 ,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24)",
"See server log for query details.",,,"SQL statement ""SELECT pg_advisory_xact_lock(999999, format('zel_event.%I', p_table_name)::regclass::oid::integer)""
Run Code Online (Sandbox Code Playgroud)

这本身就已经很奇怪了,因为看起来两个进程阻塞了同一个咨询锁,但实际上都不能抓住它。

尝试获取锁的函数如下:

CREATE OR REPLACE FUNCTION zel_event.create_new_partition(
    p_table_name text
)
RETURNS void AS
$BODY$
DECLARE
    _schema text;
BEGIN
    IF NOT EXISTS (table from catalog)
    THEN
        PERFORM pg_advisory_xact_lock(999999, format('zel_event.%I', p_table_name)::regclass::oid::integer);

        IF NOT EXISTS (table from catalog)
        THEN
            EXECUTE format('
                CREATE TABLE %1$I.%2$I (
                    LIKE zel_event.%2$I INCLUDING ALL
                ) INHERITS (zel_event.%2$I)', _schema, p_table_name);
        END IF;
    END IF;
    RETURN;

END;
$BODY$
LANGUAGE plpgsql
SECURITY DEFINER;
Run Code Online (Sandbox Code Playgroud)

在我看来,没有任何“经典”死锁原因存在,因为只有一个逻辑......
其背后的想法是按需创建新表,其中需求可以从多个连接以非常高的频率出现。当插入函数针对父表执行插入操作时调用它,由调度程序触发器将其转移到适当的子表。

这是交易的样子:

INSERT INTO parent (zel_api.insert_event())
        |
     trigger
        |
        +----> child exists? ---no---> CREATE TABLE child
                      |                         |
                     yes                        |
                      |                         |
                      V                         V
              INSERT INTO child (zel_event.create_new_partition())   
Run Code Online (Sandbox Code Playgroud)

PostgreSQL 版本是 9.2

深入了解这种情况如何发生(当然也可以减轻)会非常有趣。

Chr*_*ers 1

这不是同一个咨询锁。您的日志提到第一个进程与第二个进程有不同的表 oid(相差 10)。这看起来像一个典型的死锁情况,其中两个进程只是以不同的顺序寻求相同的锁。第一个中的第三个数字是 12864385,而第二个中的第三个数字是 12864395。

所以我怀疑两个进程都试图创建相同的分区,只是顺序不同,因此它们陷入僵局。

我发现在这种令人困惑的情况下有帮助的一件事是在一系列数字中一次遍历并比较一个数字,因为使用咨询锁很容易这样巧妙地错过一个数字。