tencent cloud

云数据库 PostgreSQL

动态与公告
产品动态
产品简介
产品概述
产品特性
产品优势
应用场景
信息安全说明
地域和可用区
产品功能列表
大版本生命周期说明
MSSQL 兼容版
产品计费
计费概述
实例类型与规格
购买方式
退费说明
欠费说明
备份空间收费说明
快速入门
创建 PostgreSQL 实例
连接 PostgreSQL 实例
管理 PostgreSQL 实例
数据导入
通过 DTS 迁移数据
内核能力介绍
内核版本概述
内核版本更新动态
查看内核版本
自研内核功能介绍
数据库审计
审计服务说明
开通审计服务
查看审计日志
修改审计服务
审计性能说明
用户指南
实例管理
升级实例
CPU 弹性扩容
只读实例
账号管理
数据库管理
参数管理
日志管理及分析
备份与恢复
数据迁移
插件管理
网络管理
访问管理
数据安全
租户及资源隔离
安全组
监控与告警
标签
AI 实践
使用 tencentdb_ai 插件调用大模型
使用 tencentdb_ai 插件构建 AI 应用
结合 Supabase 快速构建基于云数据库 PostgreSQL 的后端服务
实践教程
跨库访问
如何在 PostgreSQL 中自动创建分区
基于 pg_roaringbitmap 实现超大规模标签查找
一条 SQL 实现查询附近的人
如何配置云数据库 PostgreSQL 作为 GitLab 外部数据源
通过 cos_fdw 插件支持分级存储能力
通过 pgpool 实现读写分离
通过 auto_explain 插件实现慢 SQL 分析
使用 pglogical 进行逻辑复制
使用 Debezium 采集 PostgreSQL 数据
在 CVM 本地搭建 PostgreSQL 异地灾备环境
只读实例与只读组实践教程
如何使用云函数定时操作数据库
表膨胀处理
性能白皮书
测试方法
测试结果
API 文档
History
Introduction
API Category
Making API Requests
Instance APIs
Read-only Replica APIs
Backup and Recovery APIs
Parameter Management APIs
Security Group APIs
Performance Optimization APIs
Account APIs
Specification APIs
Network APIs
Data Types
Error Codes
常见问题
相关协议
Service Level Agreement
Terms of Service
词汇表
联系我们
文档云数据库 PostgreSQL实践教程如何在 PostgreSQL 中自动创建分区

如何在 PostgreSQL 中自动创建分区

PDF
聚焦模式
字号
最后更新时间: 2024-01-22 16:24:46
在 PostgreSQL 老版本中可通过继承支持表分区功能,如按时间,每月创建一个表分区,数据记录到对应分区中。在 PostgreSQL 10版本后,同样也支持了声明式分区了。本文将介绍如何提前创建分区或者根据写入数据实时创建分区。
下面将是常见的几种 PostgreSQL 自动创建分区表的方案。

场景

分区表在实际使用中,一般以时间字段作为分区键。如分区字段类型为 timestamp,分区方式为 List of values。 表结构如下:
CREATE TABLE tab
(
id bigint GENERATED ALWAYS AS IDENTITY,
ts timestamp NOT NULL,
data text
) PARTITION BY LIST ((ts::date));
CREATE TABLE tab_def PARTITION OF tab DEFAULT;
分区的创建一般分以下两种场景:

一、定时提前创建分区

定时提前创建分区只需一个定时任务调度工具即可实现,常见的定时任务调度工具和创建分区方法如下:

使用系统调度器,如 Crontab (Linux, Unix, etc.) 和 Task Scheduler (Windows)

以 Linux 操作系统为例,每天下午14点创建次日的分区表:
cat > /tmp/create_part.sh <<EOF
dateStr=\\$(date -d '+1 days' +%Y%m%d);
psql -c "CREATE TABLE tab_\\$dateStr (LIKE tab INCLUDING INDEXES); ALTER TABLE tab ATTACH PARTITION tab_\\$dateStr FOR VALUES IN ('\\$dateStr')";
EOF
(crontab -l 2>/dev/null; echo "0 14 * * * bash /tmp/create_part.sh ") | crontab -

使用数据库内置调度器,如 pg_cron、pg_timetable

以 pg_cron 为例,每天下午14点创建次日的分区表:
CREATE OR REPLACE FUNCTION create_tab_part() RETURNS integer
LANGUAGE plpgsql AS
$$
DECLARE
dateStr varchar;
BEGIN
SELECT to_char(DATE 'tomorrow', 'YYYYMMDD') INTO dateStr;
EXECUTE
format('CREATE TABLE tab_%s (LIKE tab INCLUDING INDEXES)', dateStr);
EXECUTE
format('ALTER TABLE tab ATTACH PARTITION tab_%s FOR VALUES IN (%L)', dateStr, dateStr);
RETURN 1;
END;
$$;

CREATE EXTENSION pg_cron;

SELECT cron.schedule('0 14 * * *', $$SELECT create_tab_part();$$);

使用专门的分区管理插件,如 pg_partman

以 pg_partman 为例,每天提前创建次日的分区表;
CREATE EXTENSION pg_partman;

SELECT partman.create_parent(p_parent_table => 'public.tab',
p_control => 'ts',
p_type => 'native',
p_interval=> 'daily',
p_premake => 1);

二、按需实时创建分区

如需按数据插入的需要来创建分区,可根据分区是否存在来判断该时间区间内有无数据的存在,一般采用触发器来实现。
需注意此方法存在以下两个问题
PostgreSQL 13及以上的版本才提供针对分区表的 BEFORE/FOR EACH ROW 触发器。
ERROR: "tab" is a partitioned table
DETAIL: Partitioned tables cannot have BEFORE / FOR EACH ROW triggers.
插入数据时,因为锁表的原因,无法修改分区表定义,即无法 ATTACH 子表, 因此必须有另一个连接来做 ATTACH 的操作,此处可以用 LISTEN/NOTIFY 机制来通知另一个连接进行分区定义的修改。
ERROR: cannot CREATE TABLE .. PARTITION OF "tab"
because it is being used by active queries in this session
ERROR: cannot ALTER TABLE "tab"
because it is being used by active queries in this session
触发器(实施子表创建和 NOTIFY)
CREATE FUNCTION part_trig() RETURNS trigger
LANGUAGE plpgsql AS
$$
BEGIN
BEGIN
/* try to create a table for the new partition */
EXECUTE
format('CREATE TABLE %I (LIKE tab INCLUDING INDEXES)', 'tab_' || to_char(NEW.ts, 'YYYYMMDD'));

/*
* tell listener to attach the partition
* (only if a new table was created)
*/
EXECUTE
format('NOTIFY tab, %L', to_char(NEW.ts, 'YYYYMMDD'));
EXCEPTION
WHEN duplicate_table THEN
NULL; -- ignore
END;

/* insert into the new partition */
EXECUTE
format('INSERT INTO %I VALUES ($1.*)', 'tab_' || to_char(NEW.ts, 'YYYYMMDD'))
USING NEW;

/* skip insert into the partitioned table */
RETURN NULL;
END;
$$;

CREATE TRIGGER part_trig
BEFORE INSERT
ON TAB
FOR EACH ROW
WHEN (pg_trigger_depth() < 1)
EXECUTE FUNCTION part_trig();
代码(实施 LISTEN 和子表 ATTACH )
#!/usr/bin/env python3.9
# encoding:utf8
import asyncio

import psycopg2
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT

conn = psycopg2.connect('application_name=listener')
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
cursor = conn.cursor()
cursor.execute(f'LISTEN tab;')


def attach_partition(table, date):
with conn.cursor() as cs:
cs.execute('ALTER TABLE "%s" ATTACH PARTITION "%s_%s" FOR VALUES IN (\\'%s\\')' % (table, table, date, date))


def handle_notify():
conn.poll()
for notify in conn.notifies:
print(notify.payload)
attach_partition(notify.channel, notify.payload)
conn.notifies.clear()

loop = asyncio.get_event_loop()
loop.add_reader(conn, handle_notify)
loop.run_forever()

总结

本文向您介绍了两种自动创建分区的方案,以下为每种方案的总结分析:
定时提前创建分区场景下的几种解决方案简单易懂,但会依赖系统或插件的定时管理机制,在运维、迁移时具有额外管理成本。
按需实时创建分区场景下,能按实际数据规律减少不必要的分区数量,但也需要较高版本(≥13)及额外连接来完成,复杂度比较高。
您可根据业务情况,选择合适的自动创建分区方式。
场景
版本
实现难易度
是否需要系统调度器或插件工具
是否需要额外连接机制
成本
定时提前创建分区
PostgreSQL 10
较简单
相对较高
按需实时创建分区
大于等于 PostgreSQL 13
较复杂
相对较低

帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈