|
| 1 | + |
| 2 | +:sectnums: |
| 3 | +:sectnumlevels: 5 |
| 4 | + |
| 5 | += pg_partman |
| 6 | + |
| 7 | +== 概述 |
| 8 | +pg_partman 用于简化和自动化对 PostgreSQL 原生声明式分区表的管理,它提供了自动创建、维护和清理分区的功能,尤其是基于时间范围和整数范围分区。 |
| 9 | + |
| 10 | +PostgreSQL 支持的 3 种分区方式:范围分区、列表分区、哈希分区。其中,pg_partman 扩展不支持哈希分区,对列表分区支持有限,它主要用于按时间(如天、周、月)或数值区间(如自增 ID)进行的范围分区。 |
| 11 | + |
| 12 | +项目地址:<https://github.com/pgpartman/pg_partman> |
| 13 | + |
| 14 | +开源协议:PostgreSQL License |
| 15 | + |
| 16 | +== 安装启用 |
| 17 | + |
| 18 | +=== 前提 |
| 19 | +已安装 IvorySQL 5.0,且 pg_config 命令在命令行能成功执行。 |
| 20 | + |
| 21 | +=== 源码安装 |
| 22 | +从 https://github.com/pgpartman/pg_partman 获取源码: |
| 23 | +[literal] |
| 24 | +---- |
| 25 | +git clone https://github.com/pgpartman/pg_partman.git |
| 26 | +cd pg_partman |
| 27 | +git switch 5.2-STABLE |
| 28 | +
|
| 29 | +make && sudo make install |
| 30 | +---- |
| 31 | + |
| 32 | +=== 启用扩展 |
| 33 | +[literal] |
| 34 | +---- |
| 35 | +-- 登录进入数据库 |
| 36 | +CREATE SCHEMA partman; |
| 37 | +CREATE EXTENSION pg_partman SCHEMA partman; |
| 38 | +---- |
| 39 | + |
| 40 | +[NOTE] |
| 41 | +pg_partman 安装启用后不会自动创建 SCHEMA,如果没有特别指定,PG 会将扩展对象默认安装到当前 search_path 中的第一个有效 SCHEMA(通常是 public)。推荐将其安装到独立 SCHEMA 中。 |
| 42 | + |
| 43 | +== 使用流程 |
| 44 | +pg_partman 本身不负责创建分区主表,它的主要功能是在已有分区表的基础上,自动创建和维护分区子表。所以在使用 pg_partman 之前,要先手动创建一个已经启动分区机制的主表。主表创建完成后,先将主表注册给 pg_partman 管理,注册时会指定分区策略等。注册成功会在数据库中创建一个命名为 template_[schema]_[table_name] 的模板表,这个模板表不会存数据,是后面所有子分区的模板。注册完成后必须通过手动调用或通过脚本自动调用 run_maintenance()/run_maintenance_proc() 来创建分区、清理分区。 |
| 45 | + |
| 46 | +下面给出两个使用案例,有其他使用需求请参考 https://github.com/pgpartman/pg_partman/blob/master/doc/pg_partman.md[pg_partman 官方文档]。 |
| 47 | + |
| 48 | +== 案例一 基于时间字段分区 |
| 49 | +场景假设:有一个日志表 logs,每天产生大量数据,希望按天自动创建子分区,并保留最近 30 天的数据,自动删除更旧的分区。 |
| 50 | + |
| 51 | +=== 手动创建分区主表 |
| 52 | +[literal] |
| 53 | +---- |
| 54 | +CREATE TABLE public.logs ( |
| 55 | + id BIGSERIAL, |
| 56 | + log_time TIMESTAMPTZ NOT NULL DEFAULT now(), |
| 57 | + message TEXT |
| 58 | +) PARTITION BY RANGE (log_time); |
| 59 | +---- |
| 60 | + |
| 61 | +=== 注册分区主表到 pg_partman |
| 62 | +注册 logs 表,按天分区,从当前时间开始预创建未来 3 天,保留最近 30 天: |
| 63 | +[literal] |
| 64 | +---- |
| 65 | +SELECT partman.create_parent( |
| 66 | + p_parent_table => 'public.logs', -- 要管理的分区父表,注意要显式带 SCHEMA |
| 67 | + p_control => 'log_time', -- 分区字段 |
| 68 | + p_interval => '1 day', -- 按天分区 |
| 69 | + p_type => 'range', -- 使用范围分区 |
| 70 | + p_premake => 3, -- 预创建3个未来的分区 |
| 71 | + p_start_partition => '2025-12-24', -- 起始分区 |
| 72 | + p_default_table => false -- 是否创建默认分区 |
| 73 | +); |
| 74 | +---- |
| 75 | + |
| 76 | +上面 SQL 会从 2025-12-24 开始按天创建分区,并且预创建以当前服务器时间为基准的未来 3 个分区,分区命名以该分区表的起始时间为后缀。 |
| 77 | + |
| 78 | +[literal] |
| 79 | +---- |
| 80 | +INSERT INTO public.logs (log_time) VALUES ('2025-12-28 00:01:00'); |
| 81 | +---- |
| 82 | + |
| 83 | +=== 注册清理(可选) |
| 84 | +[literal] |
| 85 | +---- |
| 86 | +UPDATE partman.part_config |
| 87 | +SET retention = '30 days' |
| 88 | +WHERE parent_table = 'public.logs'; |
| 89 | +---- |
| 90 | + |
| 91 | +=== 手动维护 |
| 92 | +[literal] |
| 93 | +---- |
| 94 | +select partman.run_maintenance(); |
| 95 | +---- |
| 96 | +或 |
| 97 | +[literal] |
| 98 | +---- |
| 99 | +CALL partman.run_maintenance_proc(); |
| 100 | +---- |
| 101 | + |
| 102 | +[NOTE] |
| 103 | +==== |
| 104 | +调用 run_maintenance() 时,会根据分区策略自动预生成新的分区表,预生成的基准是表的分区字段的数据。如果最近一条插入数据的 log_time 字段是 20251228,则以此为基准预生成 logs_p20251229/logs_p20251230/logs_p20251231 三张分区子表。 |
| 105 | +
|
| 106 | +另外,run_maintenance() 也会执行清理机制,清理机制以服务器时间为基准,上面的例子会将服务器当前时间 30 天之前的子表从主表记录上移除,但不会真正删除,以避免误删数据。此时 \d+ logs 查看分区主表的分区信息,看不到 30 天之前被移除的表,但 \dt 查看数据库所有的表,能看到被移除分区的子表依然存在。这些子表可以手动删除。 |
| 107 | +==== |
| 108 | + |
| 109 | +=== 自动维护 |
| 110 | +使用操作系统的 crontab: |
| 111 | + |
| 112 | +创建 shell 脚本 /usr/local/bin/partman_maintenance.sh: |
| 113 | +[literal] |
| 114 | +---- |
| 115 | +cd /usr/local/bin |
| 116 | +vi partman_maintenance.sh |
| 117 | +---- |
| 118 | +脚本内容: |
| 119 | +[literal] |
| 120 | +---- |
| 121 | +#!/bin/bash |
| 122 | +psql -U [db_username] -d [db_name] -c "CALL partman.run_maintenance_proc();" |
| 123 | +---- |
| 124 | + |
| 125 | +赋予执行权限: |
| 126 | +[literal] |
| 127 | +---- |
| 128 | +chmod +x partman_maintenance.sh |
| 129 | +---- |
| 130 | + |
| 131 | +配置执行计划: |
| 132 | +[literal] |
| 133 | +---- |
| 134 | +crontab -e |
| 135 | +# 添加一行,配置每天凌晨一点执行一次 |
| 136 | +0 1 * * * /usr/local/bin/partman_maintenance.sh |
| 137 | +---- |
| 138 | + |
| 139 | +== 案例二 基于自增ID字段分区 |
| 140 | +场景假设:创建一个订单表,每 10,000 个 ID 创建一个分区。 |
| 141 | + |
| 142 | +=== 手动创建分区主表 |
| 143 | +[literal] |
| 144 | +---- |
| 145 | +CREATE TABLE orders ( |
| 146 | + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, -- 分区键 |
| 147 | + amount NUMERIC |
| 148 | +); |
| 149 | +---- |
| 150 | + |
| 151 | +=== 注册分区主表到 pg_partman |
| 152 | +[literal] |
| 153 | +---- |
| 154 | +SELECT partman.create_parent( |
| 155 | + p_parent_table => 'public.orders', -- 要管理的分区父表,注意要带 SCHEMA |
| 156 | + p_control => 'id', -- 分区字段 |
| 157 | + p_interval => '10000', -- 每1万条数据一个分区 |
| 158 | + p_type => 'range', -- 使用范围分区 |
| 159 | + p_premake => 3, -- 预创建3个未来的分区 |
| 160 | + p_start_partition => '0', -- 起始分区 |
| 161 | + p_default_table => false -- 是否创建默认分区 |
| 162 | +); |
| 163 | +---- |
| 164 | + |
| 165 | +=== 手动维护 |
| 166 | +[literal] |
| 167 | +---- |
| 168 | +CALL partman.run_maintenance_proc(); |
| 169 | +---- |
| 170 | + |
| 171 | +== 常用操作 |
| 172 | + |
| 173 | +=== 获取被 pg_partman 管理的分区父表 |
| 174 | +[literal] |
| 175 | +---- |
| 176 | +SELECT parent_table FROM partman.part_config; |
| 177 | +---- |
| 178 | + |
| 179 | +=== 将分区父表从 pg_partman 的管理中移除 |
| 180 | +删除维护信息: |
| 181 | +[literal] |
| 182 | +---- |
| 183 | +DELETE FROM partman.part_config |
| 184 | +WHERE parent_table = 'table_name'; |
| 185 | +---- |
| 186 | + |
| 187 | +删除模板: |
| 188 | +[literal] |
| 189 | +---- |
| 190 | +DROP TABLE template_[schema]_[table_name]; |
| 191 | +---- |
0 commit comments