【问题标题】:Data inserting into Master Table after Partition分区后插入主表的数据
【发布时间】:2020-04-22 04:50:57
【问题描述】:

我正在 postgres 数据库上处理一个超过 1 TB 并且有大约 20 亿条记录的数据库表。因此,我决定根据“时间戳”列对表进行分区。

-- Step 1. 创建分区表

CREATE TABLE bigtable_y2019 (
    CHECK (timestamp >= '2019-01-01' AND timestamp < '2020-01-01')
) INHERITS (bigtable);

CREATE TABLE bigtable_y2020 (
    CHECK (timestamp >= '2020-01-01' AND timestamp < '2021-01-01')
) INHERITS (bigtable);

-- Step 2. 在键列上创建索引(时间戳)

CREATE UNIQUE INDEX bigtable_y2019_pkey ON bigtable_y2019 USING btree (id);
CREATE INDEX bigtable_y2019_timestamp ON bigtable_y2019 (timestamp);

CREATE UNIQUE INDEX bigtable_y2020_pkey ON bigtable_y2020 USING btree (id);
CREATE INDEX bigtable_y2020_timestamp ON bigtable_y2020 (timestamp); 

-- Step 3. 创建函数

CREATE OR REPLACE FUNCTION bigtable_insert_trigger()
RETURNS TRIGGER AS $$
BEGIN
IF (NEW.timestamp >= '2020-01-01' AND NEW.timestamp < '2021-01-01') THEN
        INSERT INTO bigtable_y2020 VALUES (NEW.*);
    ELSIF (NEW.timestamp >= '2019-01-01' AND NEW.timestamp < '2020-01-01') THEN
        INSERT INTO bigtable_y2019 VALUES (NEW.*);    
    ELSE
        RAISE EXCEPTION 'Date out of range.  Fix the bigtable_insert_trigger() function!';
    END IF;
    -- My understanding was this should have prevented inserting data into master table
    RETURN NULL;
END;
$$
LANGUAGE plpgsql;

-- Step 4. Enable Trigger ON BEFORE INSERT EVENT 并执行函数

CREATE TRIGGER insert_bigtable_trigger BEFORE INSERT ON bigtable FOR EACH ROW EXECUTE FUNCTION bigtable_insert_trigger();

-- Step 5. 设置 enable_partition_pruning 和 constraint_exclusion 为 ON

SET enable_partition_pruning = ON;
SET constraint_exclusion = ON;

上述这些步骤不仅在子表上插入记录,而且在父表上也插入记录,这是我试图避免的。

因此,我尝试为 AFTER INSERT 事件创建另一个触发器以删除父表。这不是最好的方法,但我想看看它是如何工作的。

--因为,tripdetail_insert_trigger 中的 RETURN NULL 并不能避免插入主表,所以我创建了一个解决方法来从主表中删除该记录。

CREATE OR REPLACE FUNCTION bigtable_mastertable_record_delete_trigger()
RETURNS TRIGGER AS $$
BEGIN
DELETE FROM ONLY bigtable WHERE id = NEW.id;
END;
$$
LANGUAGE plpgsql;

CREATE TRIGGER delete_bigtable_mastertable_record_trigger AFTER INSERT ON bigtable FOR EACH ROW EXECUTE FUNCTION bigtable_mastertable_record_delete_trigger();

父表和子表之间存在同步。如果记录被插入到子表中,那么在父表中也是如此,如果记录在其中任何一个中被删除,那么记录也会在另一个中被删除。

但是,我正在尝试根据时间戳将新记录插入到相应的子表中,并最终使父表为空,这应该基于表分区的工作原理。

【问题讨论】:

  • 您使用的是哪个 Postgres 版本?如果你真的需要分区,你应该至少使用 11 个(最好是 12 个)并使用新的声明式分区。忘记基于继承的分区。使用 12 对基表的任何插入都将自动重定向到正确的分区
  • 你原来的设置适合​​我。您做了什么来得出该行被插入到这两个地方的结论?我敢打赌你误解了一些东西。
  • @a_horse_with_no_name 我正在使用 postgres 11。目前还没有升级到 12 的计划,所以我正在尝试在版本 11 上进行这项工作。
  • @jjanes 我为 2020 年的一条记录执行了插入语句,我看到两个表中都存在相同的记录(它们具有相同的 id)。我很乐意清除更多内容。
  • Postgres 11 也会将该行路由到正确的分区。您绝对应该使用声明性分区,而不是(有些过时的)基于继承的分区。

标签: postgresql


【解决方案1】:

当使用 PostgreSQL 11 的分区功能时,父表将只是一个定义,不会包含任何条目。请参阅此处的声明性分区部分https://www.postgresql.org/docs/11/ddl-partitioning.html#DDL-PARTITIONING-DECLARATIVE

如果您有现有数据库,则需要以下步骤:

  1. 重命名旧表

    ALTER TABLE bigtable 重命名为 bigtable_pre_partitioning;

  2. 创建分区表(定义结构、索引、范围键)

    创建表大表( id int 不为空, logdate 日期不为空 ) 按范围分区 (logdate);

  3. 创建分区

    创建表 bigtable_2020_01 大表分区 从 ('2020-01-01') 到 ('2020-02-01') 的值;

  4. 创建另一个分区

    创建表 bigtable_2020_02 大表分区 从 ('2020-02-01') 到 ('2020-03-01') 的值;

  5. 附加旧分区(这需要很长时间,具体取决于您拥有多少数据)

    ALTER TABLE bigtable 附加分区 bigtable_pre_partitioning 从 (MINVALUE) 到 ('2020-01-01') 的值;

  6. 现在您可以将条目直接插入到 bigtable 父级中,它们将位于正确的分区中。

    插入大表 ...

注意事项:

  • 主键必须包含范围键
  • 如果您在父表上创建索引 -> 新分区将继承它
  • 如果在父表上创建约束 -> 新分区将继承它
  • 如果您没有匹配的分区范围,您可以创建一个默认表,所有条目都会在其中放置

希望这会有所帮助。上面链接的文章包含一个关于最佳实践的部分,非常有用。

【讨论】:

  • 谢谢丹尼尔。这完全有效。不过很抱歉我的回复晚了,因为我们在实施这些之前正在经历一些审核过程。
  • 当我在第 5 步时。(你的回答)我得到 [23514] ERROR: partition constraint is violated by some row 。这是为什么?我的数据库按范围分区()作为你的例子。
  • 很高兴听到它对 Bikash Lama 有效。 :) 你好@sofs1,pre_partitioning 表必须具有与新分区表相同的约束。如果您描述两个表,您应该查看约束是否相同。这对你有帮助吗?
  • @DanielP。我错误地在创建分区表中添加了partition by range。这就是问题所在。谢谢。
  • 完美!太好了。
【解决方案2】:

enable_partition_pruning 已在 PostgreSQL 11 中添加。 如果您使用的是 PostgreSQL 11,那么为什么不在 PostgreSQL 11 中使用表分区功能 https://www.postgresql.org/docs/11/ddl-partitioning.html

【讨论】:

  • 是的,你是对的,我使用的是 PostgreSQL 11。事实上,这是我实现分区时遵循的文档。我执行的所有步骤都来自该链接。
  • @BikashLama:您遵循了基于继承的分区的示例,但您应该遵循(相对较新的)声明性分区的示例:postgresql.org/docs/11/…
  • 在 PostgreSQL 11 中尝试声明式分区。 ```
  • 是的,我之前曾尝试过以下步骤:CREATE TABLE bigtable_y2020 (LIKE bigtable INCLUDING DEFAULTS INCLUDING CONSTRAINTS); ALTER TABLE bigtable_y2020 ADD CONSTRAINT bigtabley2019constraint CHECK ( timestamp &gt;= '2020-01-01' AND timestamp &lt; '2021-01-01' ); ALTER TABLE bigtable ATTACH PARTITION bigtable_y2020 FOR VALUES FROM ('2020-01-01') TO ('2021-01-01'); 但我收到此错误:SQL Error [42P17]: ERROR: table "bigtable" is not partitioned 根据文档:它说“无法将常规表转换为分区表,反之亦然”
  • 那么,也许剩下的唯一选择是创建启用分区的表副本,然后按照该线程中的建议复制数据并创建分区? stackoverflow.com/questions/57039108/…
猜你喜欢
  • 1970-01-01
  • 2020-02-10
  • 2018-08-20
  • 2021-12-25
  • 1970-01-01
  • 1970-01-01
  • 2019-03-02
  • 2023-03-16
  • 2022-08-20
相关资源
最近更新 更多