声明式分区改进
来自 PostgreSQL Wiki
跳转到导航跳转到搜索背景
自 10 版本以来,PostgreSQL 实现了 声明式分区.
它已经拥有许多重要功能
- 各种分区形式:RANGE / LIST / HASH;
- ATTACH PARTITION / DETACH PARTITION 命令;
- 分区剪枝 - 运行时查询优化。
还有两个替代项目 pg_pathman 和 pg_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。需要缓存。