暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Postgres 12 highlight - Functions for partitions

飞象数据 2019-03-29
368

分区在Postgres中是近几年的一个概念,从版本10开始引入并在过去几年中得到了很大的改进。通过对系统目录的特定查询来收集有关它们的信息,这是复杂的,也是可行的。但是,这些也许不能够直接被获取。例如,在处理多层分区时,获取完整分区树会导致 WITH RECURSIVE的使用。

Postgres 12在这方面有两项改进。第一个是引入了一个新的系统函数,以便轻松获取有关完整分区树的信息:

commit: d5eec4eefde70414c9929b32c411cb4f0900a2a9author: Michael Paquier <michael@paquier.xyz>date: Tue, 30 Oct 2018 10:25:06 +0900添加函数pg_partition_tree来显示与分区有关的信息。这个新函数用于显示输出给定的一个分区表的完整分区树,并且在查看多级别深层次的分区树时,避免使用任何复杂的WITH RECURSIVE查询。它返回一组记录,每个分区占一条,其中包含分区的名称、其父级分区的名称、一个布尔值和一个整数。布尔值是指示关系是否是树中的叶,整数则指示其在分区树中的级别,在分区树中,给定的表被视为根,根从零开始,并且每次向下一级扫描时加一。Author: Amit LangoteReviewed-by: Jesper Pedersen, Michael Paquier, Robert HaasDiscussion: https://postgr.es/m/8d00e51a-9a51-ad02-d53e-ba6bf50b2e52@lab.ntt.co.jp

第二个函数能够找到分区树中最顶层的父级分区:

commit: 3677a0b26bb2f3f72d16dc7fa6f34c305badacceauthor: Michael Paquier <michael@paquier.xyz>date: Fri, 8 Feb 2019 08:56:14 +0900添加函数pg_partition_root来显示分区树中的最顶层的父级分区:这在查看多层分区树时非常有用,并且,与函数pg_partition_tree结合使用,则可以通过只知道任意级别的一个成员来显示整个树。Author: Michael PaquierReviewed-by: Álvaro Herrera, Amit LangoteDiscussion: https://postgr.es/m/20181207014015.GP2407@paquier.xyz

首先让我们采用一组分区,在两个层上工作,并为所有分区定义索引:

CREATE TABLE parent_tab (id int) PARTITION BY RANGE (id);CREATE INDEX parent_index ON parent_tab (id);CREATE TABLE child_0_10 PARTITION OF parent_tab     FOR VALUES FROM (0) TO (10);CREATE TABLE child_10_20 PARTITION OF parent_tab     FOR VALUES FROM (10) TO (20);CREATE TABLE child_20_30 PARTITION OF parent_tab     FOR VALUES FROM (20) TO (30);INSERT INTO parent_tab VALUES (generate_series(0,29));CREATE TABLE child_30_40 PARTITION OF parent_tab     FOR VALUES FROM (30) TO (40)     PARTITION BY RANGE(id);CREATE TABLE child_30_35 PARTITION OF child_30_40     FOR VALUES FROM (30) TO (35);CREATE TABLE child_35_40 PARTITION OF child_30_40     FOR VALUES FROM (35) TO (40);INSERT INTO parent_tab VALUES (generate_series(30,39));

这组分区表及其分区非常简单:有直接子表的父表处理值的范围。然后其中一个子表child_30_40具有自己的分区,使用其自己范围的子集来定义。CREATE INDEX应用于所有分区,这意味着所有这些关系表在“id”列上都有一个btree索引。

首先,pg_partition_tree() 将显示完整树,输入一个关系,用作树的父级的基点,因此使用parent_tab作为输入给出完整的树:  

=# SELECT * FROM pg_partition_tree('parent_tab');    relid    | parentrelid | isleaf | level-------------+-------------+--------+------- parent_tab  | null        | f      |     0 child_0_10  | parent_tab  | t      |     1 child_10_20 | parent_tab  | t      |     1 child_20_30 | parent_tab  | t      |     1 child_30_40 | parent_tab  | f      |     1 child_30_35 | child_30_40 | t      |     2 child_35_40 | child_30_40 | t      |     2(7 rows)

如果表是属于叶分区,使用其中一个子表,则会显示其本身,或者可以给出子树:

=# SELECT * FROM pg_partition_tree('child_0_10');   relid    | parentrelid | isleaf | level------------+-------------+--------+------- child_0_10 | parent_tab  | t      |     0(1 row)=# SELECT * FROM pg_partition_tree('child_30_40');    relid    | parentrelid | isleaf | level-------------+-------------+--------+------- child_30_40 | parent_tab  | f      |     0 child_30_35 | child_30_40 | t      |     1 child_35_40 | child_30_40 | t      |     1(3 rows)

分区树的索引部分不是静止的,并且,索引所依赖的关系会被一起处理:

=# SELECT * FROM pg_partition_tree('parent_index');       relid        |    parentrelid     | isleaf | level--------------------+--------------------+--------+------- parent_index       | null               | f      |     0 child_0_10_id_idx  | parent_index       | t      |     1 child_10_20_id_idx | parent_index       | t      |     1 child_20_30_id_idx | parent_index       | t      |     1 child_30_40_id_idx | parent_index       | f      |     1 child_30_35_id_idx | child_30_40_id_idx | t      |     2 child_35_40_id_idx | child_30_40_id_idx | t      |     2(7 rows)

以下为每个字段的解释说明:

  • relid是OID(对象标识),将其作为关系的名称,用于树中的给定元素。这使用regclass作为输出以简化它的使用。

  • parentrelid是指元素的直接父级。

  • 如果元素没有自己的任何分区,则isleaf将为true。简而言之,它有物理存储空间。

  • level是一个指向树层的计数器,从0开始为最顶层父级,然后每次移动到下一层时增加1。

当涉及到使用数百个分区时,这比遍历所有目录项要快,例如使用上面提到的WITH RECURSIVE的特定查询(也可以将其绑定到SQL函数中,以提供与本文中介绍的新内核函数相同的结果)。第二个优点是,它使聚合操作更简易可读。获取给定分区树所涵盖的总物理大小可以归纳为:

=# SELECT pg_size_pretty(sum(pg_relation_size(relid)))     AS total_partition_size   FROM pg_partition_tree('parent_tab'); total_partition_size---------------------- 40 kB(1 row)

这与索引的工作方式相同,切换到pg_total_relation_size() 也会给出给定分区树使用的总物理空间,其中包含所有完整的索引集。

第二个函数pg_partition_root() 在处理复杂的分区树时非常方便。根据已使用分区的应用层策略,关系名称可以具有结构化的名称策略,仍然从一个版本到另一个版本,并且根据新功能或逻辑层的添加,这些策略可能很容易被破坏,导致混乱,并且很难弄清楚schema的形状和分区树的形状。此函数接受关系名称作为输入,并返回分区树最顶层的父级:

=# SELECT pg_partition_root('child_35_40'); pg_partition_root------------------- parent_tab(1 row)

如果输入是最顶层的父级或单个关系,那么结果是它本身:

=# SELECT pg_partition_root('parent_tab'); pg_partition_root------------------- parent_tab(1 row)=# CREATE TABLE single_tab ();CREATE TABLE=# SELECT pg_partition_root('single_tab'); pg_partition_root------------------- single_tab(1 row)

最后,有了两者的结合,可以通过了解其中一个成员来获取有关完整分区树的信息:

=# SELECT * FROM pg_partition_tree(pg_partition_root('child_35_40'));    relid    | parentrelid | isleaf | level-------------+-------------+--------+------- parent_tab  | null        | f      |     0 child_0_10  | parent_tab  | t      |     1 child_10_20 | parent_tab  | t      |     1 child_20_30 | parent_tab  | t      |     1 child_30_40 | parent_tab  | f      |     1 child_30_35 | child_30_40 | t      |     2 child_35_40 | child_30_40 | t      |     2(7 rows)

最后要注意的是,如果输入引用的关系类型不是分区树的一部分(如视图或物化视图),那么这些函数将返回NULL,而不是报错。这样可以更轻松地创建SQL查询,例如扫描pg_class,因为不需要根据关系类型创建更多WHERE过滤器。

本文翻译自 https://paquier.xyz/postgresql-2/postgres-12-partition-funcs/

文章转载自飞象数据,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论