pg_partman

1. 概述

pg_partman 用于简化和自动化对 PostgreSQL 原生声明式分区表的管理,它提供了自动创建、维护和清理分区的功能,尤其是基于时间范围和整数范围分区。

PostgreSQL 支持的 3 种分区方式:范围分区、列表分区、哈希分区。其中,pg_partman 扩展不支持哈希分区,对列表分区支持有限,它主要用于按时间(如天、周、月)或数值区间(如自增 ID)进行的范围分区。

注意,pg_partman 作为 PostgreSQL 的扩展,专门设计用于对 PostgreSQL 的分区表进行管理,所以它不支持在 Oracle 兼容模式下运行。

版本:5.2-STABLE

开源协议:PostgreSQL License

2. 安装启用

2.1. 前提

已安装 IvorySQL 5.0,且 pg_config 命令在命令行能成功执行。

2.2. 源码安装

git clone https://github.com/pgpartman/pg_partman.git
cd pg_partman
git switch 5.2-STABLE

make && sudo make install

2.3. 启用扩展

-- 登录进入数据库
CREATE SCHEMA partman;
CREATE EXTENSION pg_partman SCHEMA partman;
pg_partman 安装启用后不会自动创建 SCHEMA,如果没有特别指定,PG 会将扩展对象默认安装到当前 search_path 中的第一个有效 SCHEMA(通常是 public)。推荐将其安装到独立 SCHEMA 中。

3. 使用流程

pg_partman 本身不负责创建分区主表,它的主要功能是在已有分区表的基础上,自动创建和维护分区子表。所以在使用 pg_partman 之前,要先手动创建一个已经启动分区机制的主表。主表创建完成后,先将主表注册给 pg_partman 管理,注册时会指定分区策略等。注册成功会在数据库中创建一个命名为 template_[schema]_[table_name] 的模板表,这个模板表不会存数据,是后面所有子分区的模板。注册完成后必须通过手动调用或通过脚本自动调用 run_maintenance()/run_maintenance_proc() 来创建分区、清理分区。

下面给出两个使用案例,有其他使用需求请参考 pg_partman 官方文档

4. 案例一 基于时间字段分区

场景假设:有一个日志表 logs,每天产生大量数据,希望按天自动创建子分区,并保留最近 30 天的数据,自动删除更旧的分区。

4.1. 手动创建分区主表

CREATE TABLE public.logs (
    id BIGSERIAL,
    log_time TIMESTAMPTZ NOT NULL DEFAULT now(),
    message TEXT
) PARTITION BY RANGE (log_time);

4.2. 注册分区主表到 pg_partman

注册 logs 表,按天分区,从当前时间开始预创建未来 3 天,保留最近 30 天:

SELECT partman.create_parent(
    p_parent_table => 'public.logs',   -- 要管理的分区父表,注意要显式带 SCHEMA
    p_control => 'log_time',           -- 分区字段
    p_interval => '1 day',             -- 按天分区
    p_type => 'range',                 -- 使用范围分区
    p_premake => 3,                    -- 预创建3个未来的分区
    p_start_partition => '2025-12-24', -- 起始分区
    p_default_table => false           -- 是否创建默认分区
);

上面 SQL 会从 2025-12-24 开始按天创建分区,并且预创建以当前服务器时间为基准的未来 3 个分区,分区命名以该分区表的起始时间为后缀。

INSERT INTO public.logs (log_time) VALUES ('2025-12-28 00:01:00');

4.3. 注册清理(可选)

UPDATE partman.part_config
SET retention = '30 days'
WHERE parent_table = 'public.logs';

4.4. 手动维护

select partman.run_maintenance();

CALL partman.run_maintenance_proc();

调用 run_maintenance() 时,会根据分区策略自动预生成新的分区表,预生成的基准是表的分区字段的数据。如果最近一条插入数据的 log_time 字段是 20251228,则以此为基准预生成 logs_p20251229/logs_p20251230/logs_p20251231 三张分区子表。

另外,run_maintenance() 也会执行清理机制,清理机制以服务器时间为基准,上面的例子会将服务器当前时间 30 天之前的子表从主表记录上移除,但不会真正删除,以避免误删数据。此时 \d+ logs 查看分区主表的分区信息,看不到 30 天之前被移除的表,但 \dt 查看数据库所有的表,能看到被移除分区的子表依然存在。这些子表可以手动删除。

4.5. 自动维护

使用操作系统的 crontab:

创建 shell 脚本 /usr/local/bin/partman_maintenance.sh:

cd /usr/local/bin
vi partman_maintenance.sh

脚本内容:

#!/bin/bash
psql -U [db_username] -d [db_name] -c "CALL partman.run_maintenance_proc();"

赋予执行权限:

chmod +x partman_maintenance.sh

配置执行计划:

crontab -e
# 添加一行,配置每天凌晨一点执行一次
0 1 * * * /usr/local/bin/partman_maintenance.sh

5. 案例二 基于自增ID字段分区

场景假设:创建一个订单表,每 10,000 个 ID 创建一个分区。

5.1. 手动创建分区主表

CREATE TABLE orders (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, -- 分区键
    amount NUMERIC
);

5.2. 注册分区主表到 pg_partman

SELECT partman.create_parent(
    p_parent_table => 'public.orders', -- 要管理的分区父表,注意要带 SCHEMA
    p_control => 'id',                 -- 分区字段
    p_interval => '10000',             -- 每1万条数据一个分区
    p_type => 'range',                 -- 使用范围分区
    p_premake => 3,                    -- 预创建3个未来的分区
    p_start_partition => '0',          -- 起始分区
    p_default_table => false           -- 是否创建默认分区
);

5.3. 手动维护

CALL partman.run_maintenance_proc();

6. 常用操作

6.1. 获取被 pg_partman 管理的分区父表

SELECT parent_table FROM partman.part_config;

6.2. 将分区父表从 pg_partman 的管理中移除

删除维护信息:

DELETE FROM partman.part_config
WHERE parent_table = 'table_name';

删除模板:

DROP TABLE template_[schema]_[table_name];