简体   繁体   English

Postgresql - 附加新分区时 CHECK 约束不阻止 ACCESS EXCLUSIVE 锁和表扫描

[英]Postgresql - CHECK constraint not preventing ACCESS EXCLUSIVE lock and table scan when attaching new partition

I'm running postgresql 13.我正在运行 postgresql 13。

The below section of the postgres doc doc says I should be able to avoid a scan and ACCESS EXCLUSIVE lock to validate the partition constraint. postgres doc文档的以下部分说我应该能够避免扫描和 ACCESS EXCLUSIVE 锁来验证分区约束。

Before running the ATTACH PARTITION command, it is recommended to create a CHECK constraint on the table to be attached that matches the expected partition constraint, as illustrated above.在运行 ATTACH PARTITION 命令之前,建议在要附加的表上创建一个 CHECK 约束,该约束与预期的分区约束相匹配,如上所示。 That way, the system will be able to skip the scan which is otherwise needed to validate the implicit partition constraint.这样,系统将能够跳过验证隐式分区约束所需的扫描。 Without the CHECK constraint, the table will be scanned to validate the partition constraint while holding an ACCESS EXCLUSIVE lock on that partition.如果没有 CHECK 约束,将扫描表以验证分区约束,同时对该分区持有 ACCESS EXCLUSIVE 锁。

But, when I create a new partition with a check constraint, insert data into it, and then attach it, an ACCESS EXCLUSIVE lock is held while the table is scanned.但是,当我创建一个带有检查约束的新分区,将数据插入其中,然后附加它时,在扫描表时会持有一个 ACCESS EXCLUSIVE 锁。

The partitioned table:分区表:

CREATE TABLE IF NOT EXISTS tasks
(
    task_time timestamp(6) with time zone not null,
    task_sp_time timestamp(6) with time zone,
    task_org_id text not null,
    build_id text,
    unit_id  text,
    unit_req numeric(12,2),
    ... 30 columns truncated ...,
    constraint tasks_pkey1
        primary key (task_org_id, task_time)
)
partition by RANGE(task_time);

task_time is not null and of type timestamp (6) with timezone . task_time not null并且类型为timestamp (6) with timezone

-- create new empty partition table
CREATE TABLE tasks_partitions.tasks_20230111
(LIKE tasks INCLUDING DEFAULTS INCLUDING CONSTRAINTS);

-- add CHECK constraint on new partition 
ALTER TABLE tasks_partitions.tasks_20230111 ADD CONSTRAINT tmp_20230111
CHECK (task_time >= '2023-01-11 00:00:00+00' AND task_time <= '2023-01-11 23:59:59.999999+00');

-- select around 100 million rows into the new partition from an old default partition that has been detached.
INSERT INTO tasks_partitions.tasks_20230111
SELECT * FROM tasks_partitions.tasks_default_old where (task_time >= '2023-01-11 00:00:00+00' AND task_time <= '2023-01-11 23:59:59.999999+00');

-- attach partition
ALTER TABLE tasks ATTACH PARTITION tasks_partitions_tasks_20230111
FOR VALUES FROM ('2023-01-11 00:00:00+00') TO ('2023-01-11 23:59:59.999999+00')

Attaching the partition still holds the ACCESS EXLUSIVE lock and the entire table is scanned.附加分区仍然持有ACCESS EXLUSIVE锁,并且扫描整个表。

The tasks table did have a default partition at one point, but I detached it and renamed it in order to resolve another issue. tasks表曾经有一个默认分区,但我将其分离并重命名以解决另一个问题。 I currently do not have a default partition attached to tasks .我目前没有附加到tasks的默认分区。

When I attach the partition from the example above, I see an ACCESS EXCLUSIVE lock on the new partition and a seemingly random relation, 468140 .当我附加上例中的分区时,我看到新分区上有一个ACCESS EXCLUSIVE锁和一个看似随机的关系468140 I cannot insert any records into the tasks table while the partition is being attached and the locks are in place.在附加分区并且锁定到位时,我无法将任何记录插入到tasks表中。

If it helps, the query I run to see locks is:如果有帮助,我运行以查看锁的查询是:

SELECT a.datname,
       l.relation::regclass,
       l.transactionid,
       l.mode,
       l.GRANTED,
       l.usename,
       a.query,
       a.query_start,
       age(now(), a.query_start) AS "age",
       a.pid
FROM pg_stat_activity a
JOIN pg_locks l ON l.pid = a.pid
ORDER BY a.query_start;

The check constraint you are creating does not match the partition boundaries.您正在创建的检查约束与分区边界不匹配。 You missed this statement from the documentation :您错过了文档中的此声明:

When creating a range partition, the lower bound specified with FROM is an inclusive bound, whereas the upper bound specified with TO is an exclusive bound.创建范围分区时, FROM指定的下限是包含边界,而TO指定的上限是排他边界。

So you should define the constraint as所以你应该将约束定义为

ALTER TABLE tasks_partitions.tasks_20230111 ADD
CHECK (task_time >= '2023-01-11 00:00:00+00' AND
       task_time <  '2023-01-12 00:00:00+00');

and attach the partition with并附加分区

ALTER TABLE tasks ATTACH PARTITION tasks_partitions_tasks_20230111
FOR VALUES FROM ('2023-01-11 00:00:00+00')
             TO ('2023-01-12 00:00:00+00');

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM