当PolarDB PostgreSQL版中的数据表体积日益庞大时,手动管理分区表会变得复杂且耗时。pg_partman是一款PostgreSQL扩展,它通过自动化分区管理解决了这一痛点。该扩展能够基于时间或序列ID自动创建和维护表分区,并能执行数据保留策略,从而有效简化了分区表的生命周期管理,提升了数据库的可维护性和查询性能。
适用范围
支持的PolarDB PostgreSQL版的版本如下:
PostgreSQL 16(内核小版本2.0.16.10.11.0及以上)
PostgreSQL 14(内核小版本2.0.14.19.39.0及以上)
功能简介
pg_partman扩展的核心是自动化管理基于时间或数字/ID的分区表。它完全基于PostgreSQL内置的声明式分区功能(自pg_partman5.0.1版起),不再使用旧版的触发器方式。
其工作机制主要包括:
自动创建分区:通过后台维护任务,根据预设的间隔(如每日、每月或每100万个ID)提前创建未来的分区表,确保新数据能够无缝写入。
数据保留策略:自动分离(DETACH)或删除(DROP)过期的数据分区,有效管理数据生命周期和存储成本。
默认分区:为每个分区集创建一个默认分区,用于捕获任何不属于现有子分区的数据,防止数据丢失。您可以使用配套函数将这些数据迁移到正确的分区。
子分区支持:支持多级分区,例如将年分区再细分为日分区,以满足复杂的数据组织需求。
快速上手
本节将通过一个为日志表按天分区的完整示例,带您快速掌握pg_partman的基本用法。创建一个名为log_table的日志表,按created_at字段每天创建一个分区,并自动删除超过7天的数据。
步骤一:安装插件并创建父表
首先,在您的数据库中安装pg_partman插件,然后创建一个用于分区的父表。
-- 1. 在当前数据库中启用 pg_partman 扩展
CREATE EXTENSION pg_partman;
-- 2. 创建一个父表,用于存储日志数据
-- PARTITION BY RANGE (created_at) 指定了该表将作为分区表,并使用 created_at 列进行范围分区
CREATE TABLE public.log_table (
id bigint GENERATED BY DEFAULT AS IDENTITY,
created_at timestamptz NOT NULL,
message text
) PARTITION BY RANGE (created_at);步骤二:初始化分区集
调用create_parent()函数,将log_table注册到pg_partman的管理体系中,并定义分区规则。
-- 调用 create_parent 函数初始化分区
-- public.log_table: 要管理的父表
-- created_at: 分区键列
-- '1 day': 分区间隔,表示每天创建一个新分区
SELECT create_parent(
p_parent_table := 'public.log_table',
p_control := 'created_at',
p_interval := '1 day'
);执行成功后,pg_partman会自动创建当前及未来几天的分区。您可以通过\d+ public.log_table命令查看已创建的分区。
步骤三:插入测试数据
向父表log_table中插入一些数据,数据会根据created_at的值自动路由到对应的分区中。
INSERT INTO public.log_table (created_at, message)
SELECT generate_series(
now() - interval '4 days',
now(),
interval '1 hour'
),
'This is a log message.';步骤四:配置保留策略并验证清理
设置数据保留策略,让pg_partman自动清理过期数据。将配置log_table只保留最近2天的数据,并自动删除(DROP)过期的分区。
-- 1. 更新 part_config 表,为 log_table 设置保留策略
UPDATE part_config
SET
retention = '2 days', -- 保留周期为2天
retention_keep_table = false -- 不保留表结构,直接 DROP 过期的分区
WHERE parent_table = 'public.log_table';
-- 2. 手动执行维护任务,触发保留策略
CALL run_maintenance_proc();
-- 3. 验证:再次检查分区列表,确认2天前的分区已被删除
SELECT partition_schemaname, partition_tablename FROM show_partitions('public.log_table', 'DESC');至此,您已成功配置了一个按天分区并自动清理的日志表。
注意事项
在使用pg_partman前,请务必了解以下限制和风险,以避免数据丢失或服务中断。
属性继承:
pg_partman使用一个模板表来管理部分无法从父表直接继承的属性(如特定索引、UNLOGGED状态等)。对模板表的更改仅对新创建的分区生效,已存在的分区需要手动更新。时区:在进行基于时间的分区时,务必确保数据库系统、客户端以及所有调用维护任务的环境使用一致的时区。建议统一使用UTC,以避免因夏令时(DST)等问题导致分区创建失败或跳过。
子分区:
子分区的主要功能集中于数据的组织及生命周期管理,其对性能提升的作用有限。如果您追求性能优化,建议优先调整分区间隔,而非依赖于子分区。过多的子分区会增加管理开销,并可能耗尽
max_locks_per_transaction资源。子分区不支持逻辑复制(
PUBLICATION/SUBSCRIPTION)。 如果您计划对分区表使用逻辑复制,请避免使用子分区功能。子分区创建 (
create_sub_parent) 是破坏性操作。它会删除并重建现有的分区以添加下一级分区。在执行前,建议备份相关数据。
对象名称长度:PostgreSQL对象名有63字节的限制。
pg_partman会自动截断表名以适应后缀,但如果父表名称过长且相似,可能导致不同分区集下的分区名称冲突。建议保持父表名简洁。唯一约束:无法在分区键之外的列上创建全局唯一约束或主键,这是PostgreSQL声明式分区的原生限制。
pg_partman允许在每个分区上创建局部唯一约束,但这无法保证整个分区集的全局唯一性。默认分区:请谨慎处理落入默认分区的数据。
pg_partman会创建一个默认分区来接收不属于任何现有分区的数据。默认情况下,分区维护不会检查默认分区中的数据来创建新分区。如果数据持续写入默认分区,应尽快使用partition_data_*系列函数将其迁移至正确分区,并排查数据写入异常的原因。撤销分区:执行
undo_partition()期间会暂停所有分区维护。 在撤销分区操作完成前,run_maintenance()将不会为该分区集创建新分区或执行保留策略。锁与资源:管理大量分区可能导致锁资源消耗增加。 如果单个分区集包含数百上千个分区,
run_maintenance()或其他维护操作可能需要调整max_locks_per_transaction参数以避免耗尽内存。对于这种情况,建议使用run_maintenance_proc()过程,它会在每个分区集维护后提交事务,减少长事务带来的锁占用。
最佳实践
场景一:按天分区的日志表(含自动清理)
此场景适用于日志、审计记录、物联网(IoT)时序数据等增长迅速且有明确生命周期的数据。
业务场景
按天对audit_logs表进行分区,数据保留30天,过期数据自动归档到另一个模式,而不是直接删除。
操作步骤
创建父表和初始化分区集:
-- 创建父表 CREATE TABLE public.audit_logs ( log_id uuid NOT NULL, event_time timestamptz NOT NULL, user_id text, details jsonb, PRIMARY KEY (event_time, log_id) ) PARTITION BY RANGE (event_time); -- 初始化分区集,按天分区 SELECT create_parent( p_parent_table := 'public.audit_logs', p_control := 'event_time', p_interval := '1 day' );配置保留与归档策略:将超过30天的旧分区表移动到
archive模式下,以便后续进行离线分析或备份。-- 创建用于归档的模式 CREATE SCHEMA IF NOT EXISTS archive; -- 更新配置,设置保留策略 UPDATE part_config SET retention = '30 days', -- 保留30天 retention_schema = 'archive' -- 过期后移动到 archive 模式 WHERE parent_table = 'public.audit_logs';当
run_maintenance()运行时,任何完全早于30天前的分区表(例如audit_logs_p2023_10_01)将被从主分区集中分离,并将其模式从public更改为archive,变为archive.audit_logs_p2023_10_01。
场景二:按ID分区的业务表
此场景适用于用户表、商品表等以数字ID为主键,并希望通过分区将冷热数据分离的业务。
业务场景
对orders表按order_id进行分区,每个分区包含1000万个ID。
决策建议
p_interval的选择至关重要。应根据业务ID的增长速率和单个分区的合理大小来估算。如果每天新增100万订单,10000000的间隔意味着大约每10天创建一个新分区。
操作步骤
创建父表和初始化分区集:
-- 创建父表 CREATE TABLE public.orders ( order_id bigint NOT NULL, customer_id bigint, order_date timestamptz, amount numeric ) PARTITION BY RANGE (order_id); -- 初始化分区集,按ID范围分区 -- 注意:p_interval 即使是数字,也必须以文本形式提供 SELECT create_parent( p_parent_table := 'public.orders', p_control := 'order_id', p_interval := '10000000' -- 每个分区包含1000万个ID );配置保留策略(可选):如果旧订单数据可以被归档或删除,可以配置基于ID的保留策略。
-- 保留最近的5000万个订单数据 -- 假设当前最大 order_id 是 100,000,000 -- 该策略会删除 order_id < 50,000,000 的所有分区 UPDATE part_config SET retention = '50000000', retention_keep_table = false WHERE parent_table = 'public.orders';
场景三:迁移现有大表到分区表
此场景适用于将一个已存在的、数据量巨大的单体表(如TB级别)平滑迁移到pg_partman管理的分区结构。
业务场景
将1 TB大小的sensor_data表迁移为按月分区的分区表,并最小化对在线业务的影响。
核心工具
partition_data_proc()过程。它能以小批量、独立事务的方式迁移数据,避免了长时间的表锁和事务积压。
操作步骤
准备工作:创建父表并初始化分区集:
-- 1. 将原表重命名,作为数据源 ALTER TABLE public.sensor_data RENAME TO sensor_data_source; -- 2. 创建一个结构相同的新父表 CREATE TABLE public.sensor_data ( reading_id bigserial, device_id text, ts timestamptz NOT NULL, value double precision ) PARTITION BY RANGE (ts); -- 3. 初始化分区集 SELECT create_parent('public.sensor_data','ts','1 month');执行数据迁移:使用
partition_data_proc将sensor_data_source的数据分批迁移到新的分区表sensor_data中。-- 调用数据迁移过程 -- p_source_table: 指定数据源表 -- p_interval: 每个批次处理的数据时间范围,建议小于或等于分区间隔。此处设为'1 day',表示每天的数据在一个事务中处理。 -- p_wait: 每个批次之间的等待时间(秒),用于降低对I/O和CPU的持续压力。 CALL partition_data_proc( p_parent_table := 'public.sensor_data', p_source_table := 'public.sensor_data_source', p_interval := '1 day', p_wait := 1 );该过程会循环执行,直到
sensor_data_source表中的所有数据都被迁移完毕。迁移过程中,pg_partman会按需自动创建所需的分区。收尾工作:迁移完成后,验证数据一致性,然后可以安全地删除原数据源表
sensor_data_source。在迁移期间,应用可以继续向新的分区父表sensor_data写入新数据。
核心概念
分区类型与间隔
分区类型
pg_partman基于PostgreSQL的声明式分区,主要支持以下分区方式:
范围分区(Range Partitioning):适用于连续的数据,如时间戳或序列ID。这是最常用的分区类型。
列表分区(List Partitioning):仅在基于数字的ID分区且间隔为
1时支持。
分区间隔(p_interval)
此参数定义了每个子分区所覆盖的数据范围。
基于时间:接受任何有效的
interval值,如'1 hour'、'1 day'、'1 week'或'1 month'。说明支持的最小时间间隔为1秒,上限则受限于PostgreSQL所支持的最小和最大时间戳值。
首次运行
create_parent()创建分区集时,在确定要创建的第一个分区时,小于一天的间隔会向下取整。小于24小时但大于1分钟的间隔将向下取整到最近的小时。
小于1分钟的间隔则向下取整到最近的分钟。
系统会创建足够多的分区以支持到当前真实时间。这意味着在运行
create_parent()时,可能会创建比预期更多的先前分区,而未来分区可能不会全部创建。首次运行run_maintenance()时会修复缺失的未来分区。这是由于需要支持自定义时间间隔所导致的。对于大于或等于24小时的间隔,设置将会如预期一样进行。
对于等于或大于100年的间隔,插件将使用世纪或千年的实际起始点来确定分区名称和约束规则。例如,21世纪和第3个千年是从2001年01月01日开始的(不是2000年),这也意味着没有0年。
基于数字/ID:接受一个表示范围大小的整数,但需以文本形式提供,如
'1000000'。
周分区:每周的起始日取决于
create_parent()执行时的p_start_partition参数或当前日期。为保证周一为起始日,建议使用date_trunc()函数显式指定,例如:p_start_partition := to_char(date_trunc('week', CURRENT_TIMESTAMP), 'YYYY-MM-DD HH24:MI:SS')。epoch时间值:如果分区键是存储Unix epoch的整数列,可以通过设置
p_epoch参数(如'seconds'或'milliseconds')让pg_partman将其作为时间进行分区管理。
子分区(Sub-partitioning)
子分区允许在已有的分区上创建更细粒度的分区,形成多级分区结构(如按年分区,每年再按月子分区)。其主要用于数据组织和生命周期管理,例如对一个巨大的年分区进行按月归档。对于性能提升,子分区通常效果有限,优先考虑调整顶层分区的间隔。
子分区创建 (create_sub_parent) 是破坏性操作。它会删除并重建现有的分区以添加下一级分区。在执行前,建议备份相关数据。
数据保留策略(Retention)
pg_partman通过run_maintenance()自动执行数据保留策略,管理旧分区的生命周期。核心配置参数如下:
参数 | 描述 | 示例 |
| 保留周期。
|
|
|
|
|
| 将分离的旧分区移动到指定的归档模式,而不是保留在原模式或删除。此设置优先于 |
|
| 在分离旧分区时是否保留其上的索引。默认为 |
|
对于子分区集,当父表的一个分区被删除时,如果该分区本身又是分区表,则删除操作会级联(CASCADE)到整个继承树中的所有下级分区并全部删除。此外请注意,由pg_partman管理的分区集必须始终至少保留一个分区,因此保留策略永远不会删除一个分区集中最后一个分区。
约束排除(Constraint Exclusion)
约束排除是分区表提升查询性能的关键特性。当查询的WHERE条件可以明确排除某些分区时,查询优化器会跳过对这些分区的扫描。
pg_partman提供了在非分区键列上添加CHECK约束的功能,以增强约束排除的效果。例如,一个按created_at分区的表,如果经常有WHERE device_id = 'A'的查询,可以为旧分区(数据不再变化)的device_id列添加约束。
核心配置参数如下:
constraint_cols:一个文本数组,指定要在哪些列上创建CHECK约束。例如'{device_id, user_id}'。optimize_constraint:一个整数,定义对多老的分区应用约束。默认值30表示对30个分区间隔之前的旧分区应用约束。constraint_valid:在表上添加约束可能会与表中已有的数据产生冲突,也可能导致pg_partman的维护操作耗时较长。可以设置这些约束在创建时是否为不生效。虽然这样可以让约束的创建几乎瞬间完成,但在验证之前无法使用约束排除。因此,默认情况下约束会被创建为有效的(valid)。
此功能会限制对已添加约束的旧分区进行数据更新。如果需要修改旧数据,为了使
pg_partman能够正确管理这些约束,请不要重命名由它管理的约束,可使用reapply_constraints_proc()临时移除约束。constraint_valid在子分区(Sub-partitioning)中可能无法正常运行。它能够在第一层分区上正常执行,但在更深层的子分区表集中是否有效则依赖于所使用的分区间隔组合以及optimize_constraint的设置。例如,当将周分区进一步细分为日分区,并且日分区的optimize_constraint设置为7天时,可能无法达到预期效果。尽管周分区的约束能够被正确创建,但日分区的子分区可能不会创建相应的约束。
模板表与属性继承
由于PostgreSQL声明式分区对某些对象属性(如非分区键上的主键/唯一索引、autovacuum设置等)的继承支持不完善,pg_partman使用模板表机制来确保新创建的分区能正确继承这些属性。下表展示了pg_partman是如何管理特定属性继承的。如果某个属性未在表中列出,则表示它通过父表进行管理。
特性 | 从父表继承 | 从模板继承 |
非分区列的主键 | - | PostgreSQL 14及以上版本 |
非分区列的唯一索引 | - | PostgreSQL 14及以上版本 |
非分区列的唯一索引表空间 | - | PostgreSQL 14及以上版本 |
关系特定选项(autovacuum等) | - | PostgreSQL 14及以上版本 |
未记录表状态(UNLOGGED)* | - | PostgreSQL 14及以上版本 |
非唯一索引 | PostgreSQL 14及以上版本 | - |
权限/所有权 | PostgreSQL 14及以上版本 | - |
属性:当您调用
create_parent()时,pg_partman会自动创建一个名为parent_table_template的表。您对此模板表所做的任何ALTER TABLE修改(如添加索引、更改autovacuum参数)都会在未来创建新分区时被应用。说明对模板表的修改不会追溯应用于已存在的分区。如需修改,需要手动对旧分区执行
ALTER操作。权限和所有权:默认情况下,权限和所有权不会被继承。如果通过
pg_partman启用了该功能,请注意这种继承仅发生在分区创建时,更改后不会自动应用(可通过reapply_privileges()重新应用)。除非你需要直接访问分区,否则通常不需要启用此功能。如果你需要,可以设置inherit_privileges选项。说明如果您使用
IDENTITY特性来管理序列,则只有在通过父表插入数据时,该特性才支持自动生成新的序列值;而直接向分区插入数据时,该特性不予支持。
模板表特性只是一个临时解决方案,旨在加快声明式分区的采用。随着PostgreSQL核心功能的完善,
pg_partman将逐步淘汰模板表的使用。如果将来某个功能在PostgreSQL核心中得到支持,那么它将不再通过模板表管理,请提前规划好升级到主要版本时的相关调整。如果希望使用带有
USING INDEX子句的REPLICA IDENTITY属性来支持逻辑复制,请注意该功能仅在实际的父表上创建了所需索引的情况下得以支持,而非在模板表上。由于无法控制REPLICA IDENTITY在父表和模板表上的设置,因此无法判断哪一个才是正确的标识。为保持与其他标识继承方法(FULL和NONE)的统一,建议仅选择父表作为源。由于PostgreSQL在启用或禁用分区集的父表上的
UNLOGGED属性时存在处理不一致的问题,pg_partman使用模板表来管理UNLOGGED状态。当执行ALTER命令时,父表上的该属性实际上不会发生变化,因此新分区将继续使用更改前的属性。这意味着如果你希望将一个分区集从UNLOGGED更改为LOGGED,并让所有未来分区都继承这一更改,这是无法实现的。现在通过模板表来管理该属性后,更改模板表上的属性将使新创建的分区继承这一更改。但已存在的分区仍需手动更改,但这一直是如此。更多信息,请参见Unable to alter partitioned table to set logged。
运维与管理
自动维护
pg_partman的核心维护任务(创建新分区和执行保留策略)由run_maintenance()函数或run_maintenance_proc()过程执行。在PolarDB PostgreSQL版中,推荐使用内置的后台工作进程(Background Worker)来自动化此过程,它会根据您设置的间隔自动调用run_maintenance_proc()。
后台工作进程(Background Worker)默认关闭,由于安全原因,暂时不支持您进行配置。如有需要,请前往配额中心,在配额ID为polardb_pg_pg_cron的操作列,单击申请进行配置。
手动维护
在某些情况下,您可能需要手动触发维护,例如在调试或需要对特定表进行精细控制时。
CALL run_maintenance_proc();推荐使用此过程。它会为part_config中所有automatic_maintenance为on的分区集执行维护,并在每个分区集维护完成后自动提交事务,减少锁争用。SELECT run_maintenance('public.my_table');如果只想对单个分区集执行维护,可以调用此函数并传入父表名。
监控与告警
在生产环境中,需监控分区维护是否正常运行,以防因分区未及时创建而导致数据写入失败。part_config表中的maintenance_last_run字段记录了每个分区集最后一次成功运行维护的时间戳。这是一个关键的监控指标。
-- 查询所有自动维护的分区集中,超过2倍维护间隔仍未成功运行的
SELECT parent_table
FROM part_config
WHERE
automatic_maintenance = 'on'
AND maintenance_last_run < (now() - (SELECT setting::interval * 2 FROM pg_settings WHERE name = 'pg_partman_bgw.interval'));如果此查询返回任何行,意味着对应的分区集维护可能已中断,需要立即介入排查。
参考函数
pg_partman通过一系列函数提供其核心能力。这些函数根据其用途分为创建、迁移、维护和销毁四大类。
创建分区
此类函数用于初始化分区集和定义子分区。
create_parent()
此函数用于初始化一个分区集。它基于一个已存在并声明为分区的父表,配置其分区策略、预创建规则和维护方式。在运行期间,会对父表施加ACCESS EXCLUSIVE锁。
传递给此函数的所有选项都必须与该定义匹配。请将所有默认值、索引、约束、权限和所有权应用于父表,以便它们传播到子表。有关处理唯一索引和其他表属性的说明,请参见模板表与属性继承。
默认情况下会创建一个默认分区和一个模板表,除非另有配置。
语法
create_parent(
p_parent_table text
, p_control text
, p_interval text
, p_type text DEFAULT 'range'
, p_epoch text DEFAULT 'none'
, p_premake int DEFAULT 4
, p_start_partition text DEFAULT NULL
, p_default_table boolean DEFAULT true
, p_automatic_maintenance text DEFAULT 'on'
, p_constraint_cols text[] DEFAULT NULL
, p_template_table text DEFAULT NULL
, p_jobmon boolean DEFAULT true
, p_date_trunc_interval text DEFAULT NULL
, p_control_not_null boolean DEFAULT true
, p_time_encoder text DEFAULT NULL
, p_time_decoder text DEFAULT NULL
)
RETURNS boolean参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 是 | 无 | 分区集的父表。必须是已存在的、声明为分区的表,且必须包含模式名。 |
|
| 是 | 无 | 用作分区的控制列,如 说明 当控制列为 |
|
| 是 | 无 | 分区的间隔,参数值应作为文本传入。取值范围如下:
|
|
| 否 |
| 分区类型。取值范围如下:
|
|
| 否 |
| 当分区键列是表示时间的整数(epoch)时使用。取值范围如下:
说明 所有表名将基于时间。除了在控制列上创建一个普通索引外,还请确保在控制列上创建一个基于时间的函数索引(如 |
|
| 否 |
| 提前创建的分区数量。例如,对于日分区, 说明 某些间隔可能会因为闰年、不同月份长度等原因偶尔导致多创建一个分区或遗漏一个分区。这通常不会造成问题,并应能自动纠正(请参见分区类型与间隔)。如果分区落后于 |
|
| 否 |
| 允许手动指定第一个分区的起始值(时间戳或整数),而不是由系统自动确定。需为一个有效的时间戳(用于基于时间的分区)或正整数(用于基于数字/ID的分区)值。 说明
|
|
| 否 |
| 是否为分区集创建默认分区,用于接收不属于任何子分区的数据。 |
|
| 否 |
| 是否由全局
|
|
| 否 |
| 为旧分区添加额外 |
|
| 否 |
| 指定一个模板表。新创建的分区将从此模板继承属性(如索引、约束等)。若不指定,将自动创建。 |
|
| 否 |
| 用于对齐非标准时间间隔的分区边界。有效值为PostgreSQL内置 默认情况下, 例如,如果设置了一个9周的间隔,默认情况下 |
|
| 否 |
| 是否要求分区控制列为
|
|
| 否 |
| 当分区键为 |
|
| 否 |
| 当分区键为 |
create_sub_parent()
此函数用于为一个已存在的分区集创建子分区。这是一个破坏性操作,因为它需要删除并重建现有的分区,使其转变为新的父表。
如果计划依赖表名进行组织,建议为子分区集使用较短的表名。附加在表名末尾的后缀始终保证存在,无论该分区集使用的是哪种分区类型。较长的表名可能导致原始父表名被截断,并可能截断顶层分区后缀。这是无法控制的,并确保最低层级的分区后缀保留。
对于第一级子分区,您最初传递给
create_parent()的p_parent_table参数值应与传递给create_sub_parent()的值完全一致。如果需要进一步进行子分区,则应开始向create_sub_parent()传递不同的值(即顶级分区集的子表)。
语法
create_sub_parent(
p_top_parent text
, p_control text
, p_interval text
, p_type text DEFAULT 'range'
, p_default_table boolean DEFAULT true
, p_declarative_check text DEFAULT NULL
, p_constraint_cols text[] DEFAULT NULL
, p_premake int DEFAULT 4
, p_start_partition text DEFAULT NULL
, p_epoch text DEFAULT 'none'
, p_jobmon boolean DEFAULT true
, p_date_trunc_interval text DEFAULT NULL
, p_control_not_null boolean DEFAULT true
, p_time_encoder text DEFAULT NULL
, p_time_decoder text DEFAULT NULL
)
RETURNS boolean
参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 是 | 无 | 顶级父表的名称。此函数会将该父表下的所有分区转变为新的子分区父表。 |
|
| 是 |
| 安全确认标志。必须设置为 |
其余参数(p_control或p_interval等)与create_parent()作用相同,但定义的是子分区的策略。例如,如果您有一个按年分区的现有分区集,然后希望将每个年分区按天进行分区,可以使用此函数。
create_partition_time()/create_partition_id()
手动为基于时间或数字/ID的分区集创建特定的子分区。通常由run_maintenance()自动调用,但也可用于在维护周期外按需创建。
语法
create_partition_time(
p_parent_table text
, p_partition_times timestamptz[]
, p_start_partition text DEFAULT NULL
)
RETURNS boolean
create_partition_id(
p_parent_table text
, p_partition_ids bigint[]
, p_start_partition text DEFAULT NULL
)
RETURNS boolean参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 是 | 无 | 要为其创建子分区的父表。 |
|
| 是 | 无 |
如果分区不存在,它将被创建。如果存在,则使用现有分区并且函数仍将正常退出。请注意,给定的值将用作分区的下界,并影响分区的名称。因此,请确保给定的时间戳值与其他分区一致,否则可能会遇到值覆盖的间隙。 |
|
| 是 | 无 |
如果分区不存在,它将被创建。如果存在,则使用现有分区并且函数仍将正常退出。请注意,给定的值将用作分区的下界,并影响分区的名称。因此,请确保给定的整数值与其他分区一致,否则可能会遇到值覆盖的间隙。 |
|
| 否 |
| 用于子分区场景,指定子分区的起始值。 |
迁移数据
此类函数用于将存量数据或误入默认分区的数据迁移到正确的分区中。
partition_data_time()/partition_data_id()
将父表、默认分区或指定源表中的数据迁移到对应的分区中。如果目标分区不存在,函数会自动创建。它还可以修复插入到默认表中的数据。
如果您需要自动分区大量数据,建议使用
partition_data_proc过程以较小的批次提交数据。这将有效减少由长时间运行的事务和数据占用引起的问题。对于子分区集,必须从最高层次开始逐层进行数据分区。这意味着您必须首先运行此函数,然后执行
create_sub_parent()以创建额外的分区层级。随后,在每个新创建的子父表上再次运行此函数。
语法
partition_data_time(
p_parent_table text
, p_batch_count int DEFAULT 1
, p_batch_interval interval DEFAULT NULL
, p_lock_wait numeric DEFAULT 0
, p_order text DEFAULT 'ASC'
, p_analyze boolean DEFAULT true
, p_source_table text DEFAULT NULL
, p_ignored_columns text[] DEFAULT NULL
)
RETURNS bigint
partition_data_id(
p_parent_table text
, p_batch_count int DEFAULT 1
, p_batch_interval bigint DEFAULT NULL
, p_lock_wait numeric DEFAULT 0
, p_order text DEFAULT 'ASC'
, p_analyze boolean DEFAULT true
, p_source_table text DEFAULT NULL
, p_ignored_columns text[] DEFAULT NULL
)
RETURNS bigint
参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 是 | 无 | 分区集的父表(需包含模式名)。 |
|
| 否 |
| 一次函数调用中处理的批次数。 |
|
| 否 |
| 每个批次迁移的数据区间大小。若不指定,则使用分区表的自身间隔。 重要 如果从默认表中移动数据,则此值不得小于分区间隔。如果从非分区集的默认表源中移动数据,则可以将此间隔设置得比分区间隔小,以帮助避免在长时间运行的事务中移动大量数据。 |
|
| 否 |
| 等待行锁的秒数。 |
|
| 否 |
| 数据迁移的顺序。
|
|
| 否 |
| 是否在创建新分区后对父表执行 |
|
| 否 |
| 指定一个源表,将其数据移动到分区集中。若不指定,则从默认分区迁移。 |
|
| 否 |
| 迁移数据时要忽略的列名数组。主要用于处理含 |
partition_data_proc()
此函数是一个存储过程,它以独立的事务批次循环调用partition_data_*函数。建议在迁移大量数据时使用此过程,以避免长事务和锁占用。
语法
partition_data_proc (
p_parent_table text
, p_loop_count int DEFAULT NULL
, p_interval text DEFAULT NULL
, p_lock_wait int DEFAULT 0
, p_lock_wait_tries int DEFAULT 10
, p_wait int DEFAULT 1
, p_order text DEFAULT 'ASC'
, p_source_table text DEFAULT NULL
, p_ignored_columns text[] DEFAULT NULL
, p_quiet boolean DEFAULT false
)参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 是 | 无 | 分区集的父表。 |
|
| 否 |
| 循环执行的次数。若不设置,将处理源表中的所有数据。 |
|
| 否 |
| 作为 |
|
| 否 |
| 等待行锁的秒数。 |
|
| 否 |
| 尝试获取锁的次数。 |
|
| 否 |
| 每个批次提交后暂停的秒数,以降低写入负载。 |
|
| 否 |
| 数据迁移的顺序。
|
|
| 否 |
| 数据来源表。 |
|
| 否 |
| 迁移数据时要忽略的列。 |
|
| 否 |
| 过程不能返回值,因此默认会发出 |
维护分区
此类函数用于日常的分区管理、监控和信息查询。
run_maintenance()
pg_partman的核心维护函数,应通过cron或后台工作进程(Background Worker)定期调用。它负责自动创建新分区和执行数据保留策略。
语法
run_maintenance(
p_parent_table text DEFAULT NULL
, p_analyze boolean DEFAULT false
)
RETURNS void参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 否 |
| 维护的分区集。
|
|
| 否 |
| 是否在创建新分区后对父表运行 对于大型分区集而言, |
run_maintenance_proc()
run_maintenance() 的过程版本。它为每个分区集的维护使用独立的事务,可以减少在管理大量分区集时长事务导致的锁占用。Background Worker未使用此过程,使用标准的run_maintenance()函数。
语法
run_maintenance_proc(
p_wait int DEFAULT 0
, p_analyze boolean DEFAULT NULL
)参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 否 |
| 在维护每个分区集之间等待的秒数。 |
|
| 否 |
| 同 |
check_default()
检查所有由pg_partman管理的默认分区中是否存在数据,并返回包含数据的默认表及其行数。这是监控分区是否正常工作的重要手段。可以使用partition_data_time()和partition_data_id()将数据从这些父表/默认表移动到正确的分区中。
语法
check_default(
p_exact_count boolean DEFAULT true
)参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 否 |
| 是否返回精确的行数。设为 |
show_partitions()
列出给定分区集的所有分区。
表按照分区间隔的逻辑顺序返回,而不是按照其名称的本地排序顺序。
语法
show_partitions(
p_parent_table text
, p_order text DEFAULT 'ASC'
, p_include_default boolean DEFAULT false
)
RETURNS TABLE (
partition_schemaname text
, partition_tablename text
)参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 是 | 无 | 分区集的父表。 |
|
| 否 |
| 结果的排序方式。
|
|
| 否 |
| 是否在结果中包含默认分区。 |
show_partition_name()
根据给定的值,返回其应该归属的子分区名称,无论该分区是否实际存在。
语法
show_partition_name(
p_parent_table text
, p_value text
, OUT partition_schema text
, OUT partition_table text
, OUT suffix_timestamp timestamptz
, OUT suffix_id bigint
, OUT table_exists boolean
)
RETURNS record参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 是 | 无 | 分区集的父表。 |
|
| 是 | 无 | 一个时间或数字/ID值(以文本形式传入),用于确定其所属的分区。 说明 如果使用epoch时间分区,请给出时间戳值,而不是整数epoch值(可以使用 |
|
| - | - | 分区模式名称。 |
|
| - | - | 分区名称。 |
|
| - | - | 分区名后缀。 |
|
| - | - | 该分区是否实际存在。 |
show_partition_info()
根据分区名,返回该分区的边界值信息。
语法
show_partition_info(
p_child_table text
, p_partition_interval text DEFAULT NULL
, p_parent_table text DEFAULT NULL
, OUT child_start_time timestamptz
, OUT child_end_time timestamptz
, OUT child_start_id bigint
, OUT child_end_id bigint
, OUT suffix text
)
RETURNS record参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 是 | 无 | 分区的名称(需包含模式名)。 |
|
| 否 |
| 指定分区间隔以计算边界。若不提供,则从 |
|
| 否 |
| 提供父表名以优化内部查询。 |
|
| - | - | 如果分区集是基于时间的,则函数返回这些输出参数的值。否则返回NULL。 说明 起始值( |
|
| - | - | 如果分区集是基于整数的,则函数返回这些输出参数的值。否则返回NULL。 说明 起始值( |
|
| - | - | 输出附加到分区名中标识其内容的文本部分(不包括 |
apply_constraints()/drop_constraints()/reapply_constraints_proc()
管理在part_config的constraint_cols列中定义的额外CHECK约束。
apply_constraints():对给定分区集的分区应用约束,约束名均以partmanconstr_为前缀。说明不需要手动调用此函数来维护自定义约束。创建新分区时会自动管理向旧分区添加约束
如果
pg_partman约束已经存在于分区上,函数会跳过已存在的约束,避免重复创建。如果给定的列全部为NULL值,则不会创建约束。
如果提供了分区参数,则仅对该分区应用约束。
如果未提供分区参数,则约束将应用于最后一个比
optimize_constraint值更早的分区。例如,如果optimize_constraint值为30,则会对当前分区往前第31个分区应用约束(前提是分区预创建已保持最新)。如果需要对所有旧分区应用约束,请使用
reapply_constraints_proc过程。此方法具有选项,可以在尽可能减少性能影响的前提下简化约束应用。p_job_id用于内部用途,允许将日志合并到调用此函数的原始作业中。
apply_constraints( p_parent_table text , p_child_table text DEFAULT NULL , p_analyze boolean DEFAULT FALSE , p_job_id bigint DEFAULT NULL ) RETURNS voiddrop_constraints():删除由pg_partman为在part_config中配置的列创建的约束。这使得在需要编辑旧数据且约束不允许时,可以轻松清理约束。说明仅删除以
partmanconstr_*开头的给定分区和配置列上的约束。如果需要删除所有分区上的约束,请使用
reapply_constraints_proc过程。此方法具有选项,可以在尽可能减少性能影响的前提下简化约束删除。p_debug参数将显示使用的约束删除语句。
drop_constraints( p_parent_table text , p_child_table text , p_debug boolean DEFAULT false ) RETURNS voidreapply_constraints_proc():在所有分区上批量应用或删除约束,分批提交以避免锁占用。reapply_constraints_proc( p_parent_table text , p_drop_constraints boolean DEFAULT false , p_apply_constraints boolean DEFAULT false , p_wait int DEFAULT 0 , p_dryrun boolean DEFAULT false )
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 是 | 无 | 已创建的分区集的父表。 |
|
| 否 |
| 删除所有由 |
|
| 否 |
| 对所有比 |
|
| 否 |
| 在对一张表应用完约束删除或添加后等待的秒数,然后再继续下一张表。 |
|
| 否 |
| 运行此过程时不实际执行删除/应用约束命令。仅输出将对哪些表执行命令作为 |
其他维护函数
dump_partitioned_table_definition():生成用于重建分区集配置的create_parent()以一个用于设置part_config中存储的额外参数的UPDATE语句。说明目前仅支持单级分区集。
p_ignore_template用于生成的SQL在运行前需要先创建模板表。如果你未对模板表进行任何修改,则在此处传入true是安全的,生成的SQL将告诉pg_partman生成新的模板表。但为了安全起见,建议使用pg_dump导出模板表并在使用生成的SQL之前恢复它们,以保持任何模板覆盖设置。
dump_partitioned_table_definition( p_parent_table text, p_ignore_template_table boolean DEFAULT false ) RETURNS textpartition_gap_fill():检查并填充分区序列中可能存在的空缺分区。从当前最小的分区开始,根据分区间隔填充遇到的任何空缺,直到当前最大的分区。返回创建的分区数量。如果没有创建任何分区,则返回0。partition_gap_fill( p_parent_table text ) RETURNS integerreapply_privileges():将父表的所有权和权限重新应用到所有分区。说明父表拥有的权限将被授予所有分区,父表未拥有的权限将被撤销(
CASCADE)。被检查的权限包括:
SELECT、INSERT、UPDATE、DELETE、TRUNCATE、REFERENCES和TRIGGER。对于大型分区集而言,这可能是一个非常耗时的操作,因此该操作被设计为一个独立运行的函数。该函数仅对与父表不同的权限进行应用,但仍需对每个子分区及其所有单独权限进行系统目录的查找和比较。
reapply_privileges( p_parent_table text ) RETURNS voidstop_sub_partition():停止对某个子分区集的自动维护,同时保留其父级分区的维护。默认情况下,如果您撤销一个分区,该分区同时也是分区,除非同时撤销其父分区集,否则不会阻止该父分区集的其他分区继续进行子分区。为了解决这种情况,即您可能不希望删除父表但又不想继续创建子分区,您可以使用此函数。说明此函数仅从
part_config_sub表中删除父表条目。stop_sub_partition( p_parent_table text ) RETURNS boolean
销毁分区
此类函数用于撤销分区结构或根据保留策略删除旧分区。
undo_partition()
将一个分区集的数据从所有分区移动到单个目标表中,并解除分区结构。这是一个数据移动操作,请谨慎使用。
当运行该函数时,配置表中的
undo_in_progress列会被设置为true。这将导致所有分区的创建和保留管理停止。默认情况下,分区不会被删除(
DROP),而是被分离(DETACH)。这将使原来的分区成为空的独立表。如果没有手动设置任何批量参数,每次运行函数时将从一个分区中移动全部数据到目标表。
一旦所有分区都被取消继承/删除,配置数据将自动从
pg_partman中移除。对于子分区表,您需要从最低级别的父表开始进行反分区操作,然后再向上逐层进行。
语法
undo_partition(
p_parent_table text
, p_target_table text
, p_loop_count int DEFAULT 1
, p_batch_interval text DEFAULT NULL
, p_keep_table boolean DEFAULT true
, p_lock_wait numeric DEFAULT 0
, p_ignored_columns text[] DEFAULT NULL
, p_drop_cascade boolean DEFAULT false
, OUT partitions_undone int
, OUT rows_undone bigint)
RETURNS record
参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 是 | 无 | 分区集的父表。需带有模式名,并且需与 |
|
| 是 | 无 | 一个带有模式名的表,用于接收旧分区表的数据。 |
| int | 否 |
| 单次调用中处理的批次数。 |
|
| 否 |
| 每个批次移动的数据区间大小。可以设置得比分区间隔更小,从而将非常大的分区拆分为更小的提交批次。如果没有指定或提供的间隔大于分区间隔,则使用配置的分区间隔。注意该参数的值必须以文本形式传入。 |
|
| 否 |
| 数据迁移后是保留还是删除空的分区。
说明 至少需要两个批次才能真正从分区集中删除一个表。 |
|
| 否 |
| 用于设置在超时前等待表或行解锁的秒数。为0时,表示无限等待。 |
|
| 否 |
| 此选项允许在将数据从分区移动到目标表时过滤掉特定列。这通常仅在使用 |
|
| 否 |
| 是否级联删除子分区。仅在 说明 级联删除时,会导致删除父表时同时删除其下所有子分区表。 |
undo_partition_proc()
undo_partition()的过程版本。建议在撤销包含大量数据的分区时使用此过程,以分批提交,避免长事务。
语法
undo_partition_proc(
p_parent_table text
, p_target_table text DEFAULT NULL
, p_loop_count int DEFAULT NULL
, p_interval text DEFAULT NULL
, p_keep_table boolean DEFAULT true
, p_lock_wait int DEFAULT 0
, p_lock_wait_tries int DEFAULT 10
, p_wait int DEFAULT 1
, p_ignored_columns text[] DEFAULT NULL
, p_drop_cascade boolean DEFAULT false
, p_quiet boolean DEFAULT false
)参数说明
其参数与undo_partition()类似,并增加了部分参数用于控制批处理行为。
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 否 |
| 作为 |
|
| 否 |
| 设置过程尝试等待 |
|
| 否 |
| 在每次提交(批次)之间让过程暂停指定的秒数,以减少写入负载。 |
|
| 否 |
| 子过程不能返回值,因此默认会发出 |
drop_partition_time()/drop_partition_id()
根据保留策略,手动触发删除或分离旧表。默认情况下,旧表仅被取消继承,而不会被实际删除。建议使用配置了保留策略的run_maintenance()函数来自动删除旧表,而不是直接调用该函数。
语法
drop_partition_time(
p_parent_table text
, p_retention interval DEFAULT NULL
, p_keep_table boolean DEFAULT NULL
, p_keep_index boolean DEFAULT NULL
, p_retention_schema text DEFAULT NULL
, p_reference_timestamp timestamptz DEFAULT CURRENT_TIMESTAMP
)
RETURNS int
drop_partition_id(
p_parent_table text
, p_retention bigint DEFAULT NULL
, p_keep_table boolean DEFAULT NULL
, p_keep_index boolean DEFAULT NULL
, p_retention_schema text DEFAULT NULL
)
RETURNS int参数说明
参数 | 类型 | 是否必须 | 默认值 | 描述 |
|
| 是 | 无 | 分区集的父表。 |
|
| 否 |
| 手动指定保留策略。若不提供,则使用 |
|
| 否 |
| 是否在取消继承的同时保留或删除表。
|
|
| 否 |
| 在取消继承分区时是否保留或删除索引。
|
|
| 否 |
| 将过期的表移动到此模式下进行归档,而不是删除。 |
|
| 否 |
| (仅限时间分区)用于指定一个不同的参考时间戳,以确定哪些分区应被影响。 |
配置表
pg_partman的所有行为都由两个核心配置表驱动。在通过create_parent()或create_sub_parent()函数创建分区集时,相应的配置会自动插入这些表中。您也可以直接修改这些表中的记录来动态调整分区集的行为。
part_config
此表是pg_partman的主配置中心,存储所有顶级父表的分区策略和维护设置。
配置项(列名) | 数据类型 | 默认值 | 描述 |
|
| 无 | 分区集的父表名。 |
|
| 无 | 用作分区依据的控制列名,需为时间或整数类型的列。 |
|
| 无 | 分区的间隔。时间分区为 |
|
|
| 分区类型,目前支持 |
|
|
| 预创建的分区数量。表示在当前分区之外,始终保持存在多少个未来的分区。 |
|
|
| 控制此分区集是否由全局 |
|
| 无 | 用于新分区的模板表。新创建的分区将从此模板继承索引、约束等无法从父表继承的属性。 |
|
|
| 数据保留策略。时间分区为 |
|
|
| 当旧分区过期时,将其移动到此指定的模式下进行归档,而不是删除。此选项优先于 |
|
|
| 控制过期分区的处理方式。
|
|
|
| 控制在分离表的同时是否保留其上的索引。
|
|
|
| 控制是否将被分离的表保留在其原有的逻辑复制发布(Publication)中。
|
|
|
| 当分区键列是表示时间的整数(epoch)时使用。
|
|
|
| 一个列名数组。 |
|
|
| 定义了多旧的分区会应用 |
|
|
| 控制
|
|
|
| 是否在没有新数据写入的情况下,依然持续创建新的时间分区。
|
|
| 无 | 用于生成分区名称后缀的日期时间格式化字符串(例如 |
|
|
| 是否将父表的权限和所有权继承给所有分区。仅在需要直接访问分区时开启。
|
|
|
| 维护任务是否忽略默认分区中的数据来决定是否创建新分区。为修复问题可临时设为 |
|
|
| 定义
|
|
|
| 维护运行时要刷新的逻辑复制订阅的名称。如果分区集订阅了一个会添加/删除表的发布,并且你需要分区集感知这些更改,则必须使用此选项指定该订阅名称。否则,除非通过其他方式刷新订阅,否则订阅将永远不会感知发布者新增的表。更多信息,请参见ALTER SUBSCRIPTION。 |
|
|
| 标记子分区集是否已创建完毕。当存在大量子分区集时,允许 |
|
|
| 当 |
|
|
| 记录此分区集最后一次成功运行维护的时间戳。可用于监控指标以确保分区维护正常运行。 |
part_config_sub
此表用于存储子分区的配置。其结构与part_config基本一致,但有一个关键区别:它使用sub_parent列来标识其所属的上一级父表。
sub_parent(text):子分区集的父表名,该表本身是顶级父表的一个分区。其他列:表中所有其他的列(如
partition_interval、retention、premake等)的含义与part_config中完全相同,但其作用域是当前子分区集。
您无需在此重复查看所有列的定义。只需理解,当pg_partman维护一个子分区集时,它会从part_config_sub表中查找以sub_parent为键的配置行,并应用该行中定义的策略。