阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

PostgreSQL分区表(partitioning)应用实例

132次阅读
没有评论

共计 5936 个字符,预计需要花费 15 分钟才能阅读完成。

前言

项目中有需求要垂直分表,即按照时间区间将数据拆分到 n 个表中,PostgreSQL 提供了分区表的功能。分区表实际上是把逻辑上的一个大表分割成物理上的几小块,提供了很多好处,比如:

  1. 查询性能大幅提升
  2. 删除历史数据更快
  3. 可将不常用的历史数据使用表空间技术转移到低成本的存储介质上

那么什么时候该使用分区表呢?官方给出的指导意见是: 当表的大小超过了数据库服务器的物理内存大小则应当使用分区表 ,接下来结合一个例子具体记录一下创建分区表的详细过程。

创建分区表

首先看一下需求,现在有一张日志表,现在需要按表中的操作时间字段(operation_time)分区,如下图:
PostgreSQL 分区表(partitioning)应用实例
PostgreSQL 分区表(partitioning)应用实例

这个需求就是一个典型的按时间创建分区表,首先看一下步骤:

  1. 创建父表
  2. 创建 n 个子表,每个子表都是继承于父表
  3. 定义一个规则(Rule)或触发器(Trigger),把对主表的数据插入重定向到合适的分区表

如上所示,整体的大步骤就分为以上三个,当然还可以有一些小的优化措施,比如对于每个分区,在关键字字段上创建一个索引等等。首先来看第一步——创建父表。

在创建分区表之前应当先创建一张“父表”,所有分区表都从它继承,这个表中没有数据,也不要在这个表上定义任何检查约束及索引,现在我们就先创建这样一张表,但之前先建一个序列:

CREATE SEQUENCE "public"."t_sys_log_main_id_seq"
 INCREMENT 1
 MINVALUE 1
 MAXVALUE 99999999
 START 1
 CACHE 1;

ALTER TABLE "public"."t_sys_log_main_id_seq" OWNER TO "postgres";

接下来创建“父表”,因为是日志表,所以表名命名为“t_sys_log_main”:

CREATE TABLE "public"."t_sys_log_main" ("id" int4 DEFAULT nextval('t_sys_log_main_id_seq'::regclass) NOT NULL,
"account_affiliation_code" varchar(100) COLLATE "default" NOT NULL,
"account_affiliation" varchar(50) COLLATE "default" NOT NULL,
"operation_time" timestamp(6) NOT NULL,
"operation_key" varchar(2) COLLATE "default" NOT NULL,
"operation_value" varchar(30) COLLATE "default" NOT NULL,
"operation_loginid" varchar(100) COLLATE "default" NOT NULL,
"operation_message" varchar(300) COLLATE "default" NOT NULL,
"operation_ip" varchar(30) COLLATE "default" NOT NULL
)
WITH (OIDS=FALSE)

;
COMMENT ON TABLE "public"."t_sys_log_main" IS '系统日志表';
COMMENT ON COLUMN "public"."t_sys_log_main"."account_affiliation_code" IS '帐号所属机构代码';
COMMENT ON COLUMN "public"."t_sys_log_main"."account_affiliation" IS '帐号所属机构';
COMMENT ON COLUMN "public"."t_sys_log_main"."operation_time" IS '操作时间';
COMMENT ON COLUMN "public"."t_sys_log_main"."operation_key" IS '操作类型 (key)';
COMMENT ON COLUMN "public"."t_sys_log_main"."operation_value" IS '操作类型 (value)';
COMMENT ON COLUMN "public"."t_sys_log_main"."operation_loginid" IS '操作帐号';
COMMENT ON COLUMN "public"."t_sys_log_main"."operation_message" IS '操作信息';
COMMENT ON COLUMN "public"."t_sys_log_main"."operation_ip" IS '登录地址';

ALTER TABLE "public"."t_sys_log_main" ADD PRIMARY KEY ("id");

运行以上 DDL 语句创建父表,创建成功后接下来就可以挨个创建分区表了,由于每个分区表都是从父表继承的,所以分区表不会增加任何字段,下面我们按需求创建 4 张分区子表,分别用于存放 9 月、10 月、11 月和 12 月的日志数据:

create table t_sys_log_y2016m09
(CHECK (operation_time >= DATE '2016-09-01' AND operation_time< DATE '2016-10-01'))
INHERITS (t_sys_log_main);

create table t_sys_log_y2016m10
(CHECK (operation_time >= DATE '2016-10-01' AND operation_time< DATE '2016-11-01'))
INHERITS (t_sys_log_main);

create table t_sys_log_y2016m11
(CHECK (operation_time >= DATE '2016-11-01' AND operation_time< DATE '2016-12-01'))
INHERITS (t_sys_log_main);

create table t_sys_log_y2016m12
(CHECK (operation_time >= DATE '2016-12-01' AND operation_time< DATE '2017-01-01'))
INHERITS (t_sys_log_main);

如上所示,运行完成后即可创建 4 张分区子表,在上面的语句中我们添加了一个约束表示只允许插入本月的数据,接下来在这 4 张分区表的每个分区键上建立索引:

create index t_sys_log_y2016m09_operation_time ON t_sys_log_y2016m09(operation_time);
create index t_sys_log_y2016m10_operation_time ON t_sys_log_y2016m10(operation_time);
create index t_sys_log_y2016m11_operation_time ON t_sys_log_y2016m11(operation_time);
create index t_sys_log_y2016m12_operation_time ON t_sys_log_y2016m12(operation_time);

到此为止我们的分区表就创建完毕了,接下来需要考虑数据插入的问题,如何才能让不同日期的数据自动的插入与其对应的分区子表中呢?有两种解决方案,分别是: 规则(Rule)和触发器(Trigger),相比触发器,Rule 的开销更大,所以我在这里就不做过多介绍了,下面直接介绍 Trigger 的方式。

Trigger 通常会结合自定义函数(Function)来实现分区插入,Function 负责根据条件选择插入,而 Trigger 则负责 Function 的自动调用。首先定义 Function,功能很简单,即根据日期区间 insert 数据即可:

CREATE
OR REPLACE FUNCTION sys_log_insert_trigger () RETURNS TRIGGER AS $$
BEGIN

IF (NEW .operation_time >= DATE '2016-09-01'
    AND NEW .operation_time < DATE '2016-10-01'
) THEN
    INSERT INTO t_sys_log_y2016m09
VALUES
    (NEW .*) ;
ELSEIF (NEW .operation_time >= DATE '2016-10-01'
    AND NEW .operation_time < DATE '2016-11-01'
) THEN
    INSERT INTO t_sys_log_y2016m10
VALUES
    (NEW .*) ;
ELSEIF (NEW .operation_time >= DATE '2016-11-01'
    AND NEW .operation_time < DATE '2016-12-01'
) THEN
    INSERT INTO t_sys_log_y2016m11
VALUES
    (NEW .*) ;
ELSEIF (NEW .operation_time >= DATE '2016-12-01'
    AND NEW .operation_time < DATE '2017-01-01'
) THEN
    INSERT INTO t_sys_log_y2016m12
VALUES
    (NEW .*) ;
ELSE
    RAISE EXCEPTION 'Date out of range!' ;
END
IF ; RETURN NULL ;
END ; $$ LANGUAGE plpgsql;

最后再创建触发器用于执行刚才的 Function:

CREATE TRIGGER sys_log_insert_trigger BEFORE INSERT ON t_sys_log_main
FOR EACH ROW
EXECUTE PROCEDURE sys_log_insert_trigger();

到这里就全部创建完成了,最后测试一下看看结果。为了确认我们的触发器的确触发了,我们打开存储过程的统计开关,在 postgresql.conf 中,找到 track_functions,改成 all:
PostgreSQL 分区表(partitioning)应用实例

接下来就可以运行几条测试 insert 语句来看看是否能把指定的时间记录分别插入到与其对应的分区子表中,插入之前先看下 sys_log_insert_trigger() 的统计信息:
PostgreSQL 分区表(partitioning)应用实例

可以看到目前没有统计记录,接下来插入几条测试数据:

INSERT INTO t_sys_log_main VALUES 
(1,'200022', '西安高新第一中学初中校区', '2016-9-8 18:49:26.004', '01', 'xx', 'zhsz_t', 'test!', '127.0.0.1');
INSERT INTO t_sys_log_main VALUES 
(1,'200023', '西安高新第一中学初中校区', '2016-9-12 18:49:26.004', '01', 'xx', 'zhsz_t', 'test!', '127.0.0.1');
INSERT INTO t_sys_log_main VALUES 
(1,'200024', '西安高新第一中学初中校区', '2016-10-8 18:49:26.004', '01', 'xx', 'zhsz_t', 'test!', '127.0.0.1');
INSERT INTO t_sys_log_main VALUES 
(1,'200025', '西安高新第一中学初中校区', '2016-11-8 18:49:26.004', '01', 'xx', 'zhsz_t', 'test!', '127.0.0.1');
INSERT INTO t_sys_log_main VALUES 
(1,'200026', '西安高新第一中学初中校区', '2016-12-8 18:49:26.004', '01', 'xx', 'zhsz_t', 'test!', '127.0.0.1');
INSERT INTO t_sys_log_main VALUES 
(1,'200027', '西安高新第一中学初中校区', '2016-12-25 18:49:26.004', '01', 'xx', 'zhsz_t', 'test!', '127.0.0.1');

紧接着再看一下 sys_log_insert_trigger() 的统计信息:
PostgreSQL 分区表(partitioning)应用实例

如上图,可以看出调用了 6 次函数,因为我们插入了 6 条数据,至此分区表由创建到测试的整个过程就已经成功完成了。

总结

简单记录一下 PostgreSQL 创建分区表的完整步骤以及注意事项,希望对遇到同样问题的朋友有所帮助,The End。

———————————— 华丽丽的分割线 ————————————

在 CentOS 6.5 上编译安装 PostgreSQL 9.3 数据库 http://www.linuxidc.com/Linux/2016-06/132272.htm

CentOS 6.3 环境下 yum 安装 PostgreSQL 9.3 http://www.linuxidc.com/Linux/2014-05/101787.htm

PostgreSQL 缓存详述 http://www.linuxidc.com/Linux/2013-07/87778.htm

Windows 平台编译 PostgreSQL http://www.linuxidc.com/Linux/2013-05/85114.htm

Ubuntu 下 LAPP(Linux+Apache+PostgreSQL+PHP) 环境的配置与安装 http://www.linuxidc.com/Linux/2013-04/83564.htm

Ubuntu 上的 phppgAdmin 安装及配置 http://www.linuxidc.com/Linux/2011-08/40520.htm

CentOS 平台下安装 PostgreSQL9.3 http://www.linuxidc.com/Linux/2014-05/101723.htm

PostgreSQL 配置 Streaming Replication 集群 http://www.linuxidc.com/Linux/2014-05/101724.htm

———————————— 华丽丽的分割线 ————————————

PostgreSQL 的详细介绍 :请点这里
PostgreSQL 的下载地址 :请点这里

本文永久更新链接地址 :http://www.linuxidc.com/Linux/2016-09/135539.htm

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-22发表,共计5936字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中