删除子分区表时出现死锁

时间:2018-01-15 05:46:19

标签: postgresql database-partitioning database-deadlocks

我们有一个包含统计数据的数据库。表是分区的 根据时间使用继承。根据需要创建新的子表 根据传入的数据,应用程序运行一个夜间工作 丢掉旧儿童桌。

为了保持一致性,与一次关联的所有子表 期间在一次交易中被删除。

我们发现我们现在正在DROP序列和正常之间出现死锁 SELECT查询。下面的(大大简化)示例说明了 问题:

  1. DDL&插入虚拟行

    CREATE TABLE a(id serial primary key, t timestamp with time zone, i int);
    CREATE TABLE a1 () inherits (a);
    CREATE TABLE a2 () inherits (a);
    CREATE TABLE b(id serial primary key, id_a int, x int, y int);
    CREATE TABLE b1 () inherits (b);
    CREATE TABLE b2 () inherits (b);
    
    INSERT INTO a1(t,i) VALUES (CURRENT_TIMESTAMP, 100);
    INSERT INTO b1(id_a,x,y) VALUES (1,200,300);
    
  2. 在一个psql会话中,运行以下命令(模拟长时间运行的查询):

    SELECT pg_sleep(90) 
    FROM b
    LEFT JOIN a on a.id=b.id_a;
    
  3. 在第二个psql会话中,运行以下命令:

    BEGIN;
    DROP TABLE a2;
    DROP TABLE b2;
    
  4. 在第三个会话中,运行此命令(与第一个会话中相同的查询):

    SELECT pg_sleep(90) 
    FROM b
    LEFT JOIN a on a.id=b.id_a;
    
  5. 当在步骤2中启动的查询完成或中断时,会发生死锁。

    DETAIL:  Process 19894 waits for AccessExclusiveLock on relation 129716 of database 44449; blocked by process 20017.
    Process 20017 waits for AccessShareLock on relation 129700 of database 44449; blocked by process 19894.
    

    问题很容易发现:DROP事务所采取的锁定是a2然后是b2的顺序,但SELECT的顺序是相反的。 声明。似乎SELECT语句按顺序采用它们的锁 查询中的连接。

    我们尝试在删除表之前使用单个LOCK命令锁定表:这没有帮助,它们按照LOCK命令中列出的顺序被锁定,如果与命令不同,死锁仍然会发生它们出现在所有SELECT查询中。

    我们不希望为所有用户查询强制执行特定的加入订单,以确保我们在过期旧数据时不会出现死锁。

    有一件事我们发现,根据我们试图执行的SELECT查询,我们使用一个连接顺序与另一个连接顺序相比会得到更糟糕的性能(我知道这不应该是,但是当我们选择时,规划者会选择更好的计划使用一个连接顺序与另一个连接顺序。)

    另一方面,查询是根据用户输入动态生成的 并全面强制执行连接顺序将限制可以进行的查询 执行(现实生活中的查询要复杂得多,并且可能涉及很多 表,视图,子查询等)。

    我认为连接顺序决定了SELECT查询的锁定顺序是正确的吗?

    有没有办法在不强制执行特定连接的情况下避免这些死锁 只读SELECT查询的顺序?

    我们正在使用Postgresql 9.6.6。

1 个答案:

答案 0 :(得分:0)

处理表的顺序不一定是它们在查询中出现的顺序。但是,在计划期间确定该连接顺序,并且在计划期间,将首先按照它们在查询中出现的顺序处理(和锁定)表。

您说要删除的所有表都是继承子项,因此我很惊讶您的查询直接访问分区而不是父表。如果所有查询都访问父表,则应该能够通过在删除任何继承子项之前将父表锁定在ACCESS EXCLUSIVE模式来避免死锁。