【问题标题】:Postgres is taking too long to attach partition to a table. Want to understand whyPostgres 将分区附加到表的时间太长。想明白为什么
【发布时间】:2023-02-22 15:24:24
【问题描述】:

我有一张表 T1(未分区)。 T1 的大小约为 4TB。我创建了另一个表 T2(分区表)。现在我想将 T1 附加为 T2 的子表。所以我在查询下面运行以实现相同的目的。

ALTER TABLE T1
  ADD CONSTRAINT partition_check_skip 
  CHECK ( "created_at" BETWEEN ( '-infinity' ) 
  AND ( DATE_TRUNC('week', CURRENT_DATE::timestamp) + '7 days'::interval )) NOT VALID;

ALTER TABLE public.T2
  ATTACH PARTITION T1 FOR VALUES FROM 
  ('-infinity') TO (DATE_TRUNC('week', CURRENT_DATE::timestamp) + '7 days'::interval);

即使我提到了NOT VALID,Postgres 仍然花费太长时间将 T1 附加为 T2 的分区。

T1 架构

CREATE TABLE T1
(
  uuid         uuid DEFAULT gen_random_uuid() NOT NULL,
  created_at   timestamp WITHOUT TIME ZONE    NOT NULL,
  updated_at   timestamp WITHOUT TIME ZONE    NOT NULL
);

T2 架构

CREATE TABLE T2
(
  uuid         uuid DEFAULT gen_random_uuid() NOT NULL,
  created_at   timestamp WITHOUT TIME ZONE    NOT NULL,
  updated_at   timestamp WITHOUT TIME ZONE    NOT NULL
) PARTITION BY RANGE (created_at);

尝试添加NOT VALID,但仍然花费太长时间。


以下是实际的 SQL 语句:

非分区表

CREATE TABLE public.random_txn_old (
    id int8 NOT NULL DEFAULT id_generator(),
    ref_id text NULL,
    txn_ref_id text NULL,
    msg_id text NULL,
    api text NULL,
    request_payload jsonb NULL,
    response_payload jsonb NULL,
    biller_request jsonb NULL,
    biller_response jsonb NULL,
    status text NULL,
    created_at timestamp NOT NULL DEFAULT current_timestamp_utc(),
    modified_at timestamp NOT NULL DEFAULT current_timestamp_utc(),
    is_deleted bool NULL DEFAULT false,
    CONSTRAINT t2_check CHECK (((created_at >= '2021-02-23 00:00:00') AND (created_at <= '2023-02-24 00:00:00'))),
    CONSTRAINT transaction_old_pkey PRIMARY KEY (id, created_at)
);

CREATE INDEX transaction_ref_id ON public.random_txn_old (ref_id);

CREATE INDEX txn_ref_api_idx ON public.random_txn_old (txn_ref_id, api);

表触发器:

create trigger set_timestamp_txn before
update
    on
    public.random_txn_old for each row execute function trigger_set_timestamp_modify();

分区表:

CREATE TABLE public.random_table (
    id int8 NOT NULL DEFAULT id_generator(),
    ref_id text NULL,
    txn_ref_id text NULL,
    msg_id text NULL,
    api text NULL,
    request_payload jsonb NULL,
    response_payload jsonb NULL,
    biller_request jsonb NULL,
    biller_response jsonb NULL,
    status text NULL,
    created_at timestamp NOT NULL DEFAULT current_timestamp_utc(),
    modified_at timestamp NOT NULL DEFAULT current_timestamp_utc(),
    is_deleted bool NULL DEFAULT false,
    CONSTRAINT transaction_part_pkey PRIMARY KEY (id, created_at)
)
PARTITION BY RANGE (created_at);

CREATE INDEX transaction_part_ref_id ON ONLY public.random_table (ref_id);

CREATE INDEX txn_part_ref_api_idx ON ONLY public.random_table (txn_ref_id, api);

我想做什么?

尝试使用此查询将 random_txn_old 附加到 random_table:

ALTER TABLE random_table
     ATTACH PARTITION random_txn_old FOR VALUES FROM ('2021-02-23 00:00:00.000')
        TO ('2023-02-24 00:00:00.000');

【问题讨论】:

  • 您必须先验证约束。此外,预期分区是否具有所有必需的索引?您确定created_atNOT NULL 了吗?
  • 我想跳过冗长的验证检查,这就是我指定 NOT VALID 的原因。是的,created_atNOT NULL。我没有添加 T1 中存在的所有索引。目前 T1 和 T2 只有索引 t_index(uuid, created_at)
  • 为了避免在将分区附加到父表时进行验证检查,您需要一个有效的检查约束。 NOT VALID 不是你想要的,让它有效。
  • 但是添加有效支票需要一些时间,对吧?为此,我们必须在生产数据库中执行此操作时停机。还要查看此文档 postgresql.org/docs/13/sql-altertable.html 提到 NOT VALID 可以跳过冗长的扫描
  • “为此,我们必须在生产数据库中执行此操作时停机”为什么?验证约束不会对表进行强锁定。这就是执行两步过程的重点。

标签: postgresql postgresql-13


【解决方案1】:

T1 必须满足许多条件才能快速运行:

  • 该表必须具有可用于分区表上所有索引的分区的索引

  • 必须有一个与分区约束相匹配的检查约束并且(除非该列被定义为NOT NULL)禁止 NULL 值

您创建了一个与分区约束相匹配的约束,因此请确保列定义为NOT NULL 并验证约束:

ALTER TABLE t1 VALIDATE CONSTRAINT partition_check_skip;

这不会妨碍桌面上的工作。确保您拥有所有必需的索引,然后将表附加为分区应该很快。


在您的情况下,检查约束未正确定义。代替

created_at <= '2023-02-24 00:00:00'

你必须像这样测试范围的上限:

created_at < '2023-02-24 00:00:00'

这是因为上端不包括在范围分区中。

【讨论】:

  • 首先感谢详细的解答。我做了什么? 1. 添加T2T1 中的所有其他索引 2. 使用ALTER TABLE t1 VALIDATE CONSTRAINT partition_check_skip; 触发验证约束仍然没有运气。自上次 1 小时以来,附加分区进程仍在运行。
  • 如果没有看到确切的 CREATE TABLECREATE INDEX 语句(或 psqld tablename 的输出),我无法提供更好的帮助。您可以通过查看 pg_stat_progress_* 视图(如果它是一个索引)或通过使用调试器附加到后端并检查它在做什么(stack trace 可能就足够了)来研究什么正在花费时间。
  • 请查找表格DDL
  • 有了这些附加信息,情况就很清楚了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-23
  • 2015-12-16
  • 1970-01-01
  • 2012-07-31
  • 1970-01-01
相关资源
最近更新 更多