声明式分区改进

来自 PostgreSQL Wiki
跳转到导航跳转到搜索

背景

自 10 版本以来,PostgreSQL 实现了 声明式分区.

它已经拥有许多重要功能

  • 各种分区形式:RANGE / LIST / HASH;
  • ATTACH PARTITION / DETACH PARTITION 命令;
  • 分区剪枝 - 运行时查询优化。

还有两个替代项目 pg_pathmanpg_partman,它们是与声明式分区并行开发的。

它们包含不同的功能集,但最重要的是,它们实现了简化分区管理的功能。

在这个项目中,我想将这些功能引入 PostgreSQL 核心。

拟议改进

1. 自动分区 (静态)

之前的讨论最终提出了一个语法提案。参见 至少在 HASH 上自动创建分区?

这是一个相对简单的任务。但我们必须就新的语法达成一致,并使其可扩展以适应未来的改进。

2. 自动分区 (动态)

这个问题至少有两个微妙的点需要讨论

  • 我们需要将用于动态生成分区边界的信息存储在目录中的某个地方。
  • 我们需要实现特定的锁定来处理并发插入。乍一看,它看起来类似于现有需要多个步骤的并发操作。

3. 分区创建事件处理回调

此功能在 pg_pathman 中存在,被广泛用于重命名新创建的分区和清除旧数据。可能最好的解决方案是扩展事件触发器,但作为开始,我们可以重用 pg_pathman 的方法,使用用户定义的回调。参见 set_init_callback() 获取更多详细信息。

4. 现有分区的 SPLIT 和 MERGE

5. 现有表的并发分区

语法讨论

其他 DBMS

其他 DMBS 中现有分区语法的概述。这里的示例是草图,旨在展示最重要的或特定于 DBMS 的语法细节。子分区语法也被省略。请点击链接阅读完整的语法文档。

1) Greenplum

  • 不涵盖 HASH 分区。
  • 列表
PARTITION BY LIST (gender)
( PARTITION girls VALUES ('F'), 
 PARTITION boys VALUES ('M'), 
 DEFAULT PARTITION other );
  • 范围。允许显式指定 INCLUSIVE|EXCLUSIVE 边界。
PARTITION BY RANGE (date)
( START (date '2016-01-01') INCLUSIVE
  END (date '2017-01-01') EXCLUSIVE
  EVERY (INTERVAL '1 day') );

2) MySQL

PARTITION BY 
{ HASH(expr) [ PARTITIONS num]
| RANGE (expr) 
| LIST (expr)
[(partition_definition [, partition_definition] ...)]
partition_definition:
PARTITION partition_name
[VALUES
{LESS THAN {(expr | value_list) | MAXVALUE}
|  IN (value_list)}]

3) Oracle. 完整的语法非常复杂。基本语法看起来或多或少与上面的 MySQL 语法类似。

动态范围分区称为 "interval partitioning"

PARTITION BY RANGE(expr)
INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'))
( PARTITION P_DEC2018 VALUES LESS THAN (TO_DATE('01-JAN-2019','DD-MON-YYYY')));

4) MsSQL 具有非常特殊的语法,所以我甚至不会考虑这种方式。表定义使用 分区方案,它反过来需要一个分区函数。

提案 (可能会有所变化)

当前提案主要基于 Greenplum 语法。

静态

  • 适用于所有类型:HASH、LIST 和 RANGE
  • LIST 和 RANGE 也支持默认分区
CREATE TABLE numbers(int number)
PARTITION BY HASH (number)
USING (partition_desc)

其中 partition_desc 为

MODULUS n
| VALUES IN (value_list), [DEFAULT PARTITION part_name]
| START ([datatype] 'start_value')
  END ([datatype] 'end_value')
  EVERY (partition_step), [DEFAULT PARTITION part_name]

其中 partition_step 为

[datatype] [number | INTERVAL] 'interval_value'

示例

CREATE TABLE numbers(int number)
PARTITION BY HASH (number)
USING (MODULUS 3)
CREATE TABLE letters(char letter)
PARTITION BY LIST (letter)
USING
(VALUES IN (('a', 'b'), (‘c’,’d’)),
DEFAULT PARTITION other_letters);
CREATE TABLE years(int year)
PARTITION BY RANGE (year)
USING
(START (2006) END (2016) EVERY (1),
DEFAULT PARTITION other_years);

动态

  • 仅适用于 RANGE 分区。如果 END 边界未设置,则应用动态分区而不是静态分区。
CREATE TABLE years(int year)
PARTITION BY RANGE (year)
USING
(START (2006) EVERY (1));
  • 为了处理递减值,请设置负的 'partition_step'。
  • 第一个分区与父表一起创建,以将 partbound 值保存在目录中,其他分区是在运行时创建的。“partition_step”存储在父表的 pg_class.relpartstep 新列中。
  • 如果我们有太多分区,partbound 计算可能需要时间。我们需要找到最右边的/最左边的分区,并添加 partition_step。需要缓存。