Skip to content

pg_pathman 分区表

介绍

分区表的诉求在现实的生成中的意义不必多说,pg以前的实现方式多采用触发器,rules实现。数据量上来时性能明显不尽如意。
虽然pg10 ,11 版本在分区表的特性上不断发力。但是性能啥还是不够给力。
pg_pathman 分区表功能在目前的pg版本10.6 中优势还是非常明显的。

在期待pg自身分区表特性的同时,当前的pg10中还是使用pg_pathman来实现分区功能吧。

pathman与pg11 对比

优点:
支持HASH和RANGE分区,后续会支持LIST分区 支持自动和手动的分区维护
为分区表生成更有效的执行计划 通过引入两个自定义的执行计划节点RuntimeAppend & RuntimeMergeAppend,
实现运行时动态添加分区到执行计划中 为新插入数据自动创建分区(只对RANGE分区) 提供用户callbacks接口处理创建分区事件。
提供在线分区实施(在线重定义),父表数据迁移到子表,拆分, 合并分区

不足:
不支持list分区;不支持二级分区;权限,索引,trigger等无法继承; 修改主键默认的seq需要重建分区。

PG11内置分区
优点:
支持hash,range,list分区 支持多字段组合分区,支持表达式分区 支持创建主键,外键,索引,分区表自动继承。 支持update分区键 支持分区表DETACH,ATTACH,支持二级分区 分区自动创建
Default partition Partition improvements

不足:

在主表添加权限,索引,trigger等无法继承 分区表不可以作为其他表的外键主表

分区表数量对插入数据的影响

https://www.jianshu.com/p/1cba77d18694

pathman 分区表 转换为原生分区表

https://github.com/digoal/blog/blob/master/201911/20191113_01.md

主要思路

1 创建一个与原来分区表一样的主表包括分区方式 。

2 将原来的主表上的分区都卸载为普通表,在重新按照原生分区表的方式挂载上去。

直接2 也行

拓展思考。 分区数据迁移使用pg_pathman,迁移后再转换到原生表。

注意事项

需要将pg_pathman放在后面注册,如pg_stat_statements。

shared_preload_libraries = 'pg_stat_statements,pg_pathman'

创建拓展

CREATE SCHEMA pathman;
GRANT USAGE ON SCHEMA pathman TO PUBLIC;
CREATE EXTENSION pg_pathman WITH SCHEMA pathman;

参考

https://github.com/postgrespro/pg_pathman

https://github.com/digoal/blog/blob/362b84417ca8b7aaf1add31fe7689c347642bb9a/201610/20161024_01.md

常见错误

FATAL:  could not load library "/usr/pgsql-12/lib/pg_pathman.so": /usr/pgsql-12/lib/pg_pathman.so: undefined symbol: expandTableLikeClause
postgres 版本问题

简单应用

1 创建表分区,禁止数据迁移

2 并行迁移数据

3 禁止主表

表 log 必需满足

  • 字段 created_time not null

  • 无外键约束

按月分表,后续数据超出范围会自动创建分区(默认)

查看表中最早日期

select min(created_time) from log;
---
2018-05-18 00:00:00

分表 false 表示禁止数据移动

select create_range_partitions('log'::regclass,'created_time','2018-05-18 00:00:00'::timestamp,interval '1 month', null,false);

查看分区表

select * from pathman_partition_list where parent = 'log'::regclass;

并行迁移数据

select partition_table_concurrently('log'::regclass,10000,1.0);

查看迁移状态

select * from pathman_concurrent_part_tasks ;

禁主表

select set_enable_parent('log'::regclass,false);

查看数据

select count(1) from only log;

分区表常用管理

将一个分区拆分为两个分区

split_range_partition(partition_relid REGCLASS,
                      split_value     ANYELEMENT,
                      partition_name  TEXT DEFAULT NULL,
                      tablespace      TEXT DEFAULT NULL)

合并多个连续分区,数据将到第一个分区

merge_range_partitions(variadic partitions REGCLASS[])

向后追加一个分区,分区间隔默认

append_range_partition(parent_relid   REGCLASS,
                       partition_name TEXT DEFAULT NULL,
                       tablespace     TEXT DEFAULT NULL)

向前追加一个分区,分区间隔默认

prepend_range_partition(parent_relid   REGCLASS,
                        partition_name TEXT DEFAULT NULL,
                        tablespace     TEXT DEFAULT NULL)

添加一个自定义间隔分区: 如加一个

add_range_partition(parent_relid   REGCLASS,
                    start_value    ANYELEMENT,
                    end_value      ANYELEMENT,
                    partition_name TEXT DEFAULT NULL,
                    tablespace     TEXT DEFAULT NULL)

删除一个分区,及数据是否删除. 不删除数据将入主表

drop_range_partition(partition TEXT, delete_data BOOLEAN DEFAULT TRUE)

卸载分区为普通表

detach_range_partition(partition_relid REGCLASS)

挂载普通表为分区表

attach_range_partition(parent_relid    REGCLASS,
                       partition_relid REGCLASS,
                       start_value     ANYELEMENT,
                       end_value       ANYELEMENT)

参数

修改默认分区间隔

set_interval(relation REGCLASS, value ANYELEMENT)

是否禁用主表,禁用后执行计划将不在走主表

set_enable_parent(relation REGCLASS, value BOOLEAN)

是否自动创建分区. 开启后注意事项, 如果有一条数据的时间异常,会创建大量的分区表。灾难

set_auto(relation REGCLASS, value BOOLEAN)

遗留问题

1 原表分区后数据磁盘占用增加近一倍,需要vacuum full 解决. 主表残留

数据全部分区后 vacuum 速度也很快

2 分区后对父表添加或删除索引操作对现有分区表不产生作用,仅对新生成的分区有效。How do I create indexes?

注意事项

对已经分区的表使用copy 方式导入数据后数据只存在于父表中,此时执行partition_table_concurrently 无效果

解决

 1 set_enable_parent('log'::regclass, true)
 2 创建分区表 如插入一条数据 , 时间比最小时间还小,select min(create) from only log
 3 partition_table_concurrently

分区表与原生表比较。管理上带来了很大的便利,尤其是数据的归档整理。

性能上反而可能会变得更差,查询条件一定要带上分区健,否则会扫描所有子表。

当单个索引的大小超过物理内存的一半时考虑分表

遇见过的错误

ERROR:  unrecognized node type: 369
背景:  SQL statement "select public.create_single_range_partition

解决: https://github.com/postgrespro/pg_pathman/issues/224

将 1.5.11 升级至 1.5.12