在postgresql数据库中由于单表默认上限是32TB,遇到较大容量的表就需要使用分区表对主表进行拆分。方便管理,也能大大 提高对数据的检索效率。
分区表分为四种类型
目录
分区表简介
继承式分区
解除父子表的及继承关系
列值分区
增减分区表(列值分区)
范围分区
增减分区表(范围分区)
哈希分区
增减分区表(哈希分区)
删除继承式分区表
删除声明式分区表
范围分区最为常见,也最为灵活,它的分区键可以是数值,时间。并根据其值划分范围进行分区。相当于应用了 between start_vaules and end_vaules and 不等于end_vaules的一个判断。其分区范围规则是左闭右开[start_values,end_values)
列表分区它的分区键可以是数值,时间,字符串。并根据其具体值进行分区、相当于是等值判断
哈希分区它的分区键可以是任何值。数据库会根据插入分区键的值通过内置的哈希函数计算其返回值为整型的哈希值,再根据这个哈希值进行计算取模求余数,不同的余数就会被传入到不同的子表中去
继承式分区,通过before insert on 父表name for each row 触发器去触发存储过程,并通过存储过程内部的if 判断分区键的值而插入到不同的子表中去。if判断可以是等值判断,范围判断,也可以是哈希值的等值、范围判断。
在pg10.0版本以前,主流还是使用继承式分区,但是继承式分区其创建环节也及其繁琐,需要创建父表➡子表➡函数➡触发器 环环相扣,由于链路的繁杂,也导致其维护较为复杂,当需要增加子表时,还需要修改存储过程以及检查触发器的涉及内容。
原理:当有数据进入目标表时,会在其插入目标表前启动触发器,而触发器会调用存储过程,存储过程会去判断该插入的数据其分区键的值,根据分区键的值插入到对应的分区子表中去
--继承式分区
CREATE TABLE public.text_import (
代码 varchar(200) NULL,
名称 varchar(200) NULL,
物料全名 varchar(200) NULL,
规格型号 varchar(200) NULL,
辅助属性类别_fname varchar(200) NULL,
辅助属性类别_fnumber varchar(200) NULL,
物料属性_fname varchar(200) NULL,
默认仓库_fname varchar(200) NULL,
默认仓库_fnumber varchar(200) NULL,
来源_fname varchar(200) NULL,
id bigserial NOT NULL
);
--创建子表
CREATE TABLE public.text02 (
CONSTRAINT text02_id_check CHECK (( (id <= 100000)))
)
INHERITS (public.text_import);
CREATE TABLE public.text03 (
CONSTRAINT text03_id_check CHECK (((id >= 100001) AND (id <= 200000)))
)
INHERITS (public.text_import);
CREATE TABLE public.text04 (
CONSTRAINT text04_id_check CHECK (((id >= 200001) AND (id <= 300000)))
)
INHERITS (public.text_import);
CREATE TABLE public.text05 (
CONSTRAINT text05_id_check CHECK (((id >= 300001) AND (id <= 400000)))
)
INHERITS (public.text_import);
CREATE TABLE public.text06 (
CONSTRAINT text06_id_check CHECK (((id >= 400001) AND (id <= 500000)))
)
INHERITS (public.text_import);
CREATE TABLE public.text07 (
CONSTRAINT text07_id_check CHECK (((id >= 500001) AND (id <= 600000)))
)
INHERITS (public.text_import);
CREATE TABLE public.text08 (
CONSTRAINT text08_id_check CHECK (((id >= 600001) AND (id <= 700000)))
)
INHERITS (public.text_import);
CREATE TABLE public.text09 (
CONSTRAINT text09_id_check CHECK (((id >= 700001) AND (id <= 800000)))
)
INHERITS (public.text_import);
CREATE TABLE public.text10 (
CONSTRAINT text10_id_check CHECK (((id >= 800001) AND (id <= 900000)))
)
INHERITS (public.text_import);
CREATE TABLE public.text11 (
CONSTRAINT text11_id_check CHECK ((id >= 1000000))
)
INHERITS (public.text_import);
--创建存储过程
create or replace function ins_tg() returns trigger as $$
declare
begin
if new.ID <= 100000 then
insert into TEXT02 (
代码
,名称
,物料全名
,规格型号
,辅助属性类别_fname
,辅助属性类别_fnumber
,物料属性_fname
,默认仓库_fname
,默认仓库_fnumber
,来源_fname
,id)values(NEW.*);
elseif new.ID >= 100001 AND NEW.ID <= 200000 then
insert into TEXT03(
代码
,名称
,物料全名
,规格型号
,辅助属性类别_fname
,辅助属性类别_fnumber
,物料属性_fname
,默认仓库_fname
,默认仓库_fnumber
,来源_fname
,id)values(NEW.*);
elseif new.ID >= 200001 AND new.ID <= 300000 then
insert into TEXT04(
代码
,名称
,物料全名
,规格型号
,辅助属性类别_fname
,辅助属性类别_fnumber
,物料属性_fname
,默认仓库_fname
,默认仓库_fnumber
,来源_fname
,id)values(NEW.*);
elseif new.ID >= 300001 AND new.ID <= 400000 then
insert into TEXT05(
代码
,名称
,物料全名
,规格型号
,辅助属性类别_fname
,辅助属性类别_fnumber
,物料属性_fname
,默认仓库_fname
,默认仓库_fnumber
,来源_fname
,id)values(NEW.*);
elseif new.ID >= 400001 AND new.ID <= 500000 then
insert into TEXT06(
代码
,名称
,物料全名
,规格型号
,辅助属性类别_fname
,辅助属性类别_fnumber
,物料属性_fname
,默认仓库_fname
,默认仓库_fnumber
,来源_fname
,id)values(NEW.*);
elseif new.ID >= 500001 AND new.ID <= 600000 then
insert into TEXT07(
代码
,名称
,物料全名
,规格型号
,辅助属性类别_fname
,辅助属性类别_fnumber
,物料属性_fname
,默认仓库_fname
,默认仓库_fnumber
,来源_fname
,id)values(NEW.*);
elseif new.ID >= 600001 AND new.ID <= 700000 then
insert into TEXT08(
代码
,名称
,物料全名
,规格型号
,辅助属性类别_fname
,辅助属性类别_fnumber
,物料属性_fname
,默认仓库_fname
,默认仓库_fnumber
,来源_fname
,id)values(NEW.*);
elseif new.ID >= 700001 AND new.ID <= 800000 then
insert into TEXT09(
代码
,名称
,物料全名
,规格型号
,辅助属性类别_fname
,辅助属性类别_fnumber
,物料属性_fname
,默认仓库_fname
,默认仓库_fnumber
,来源_fname
,id)values(NEW.*);
elseif new.ID >= 800001 AND new.ID <= 900000 then
insert into TEXT10(
代码
,名称
,物料全名
,规格型号
,辅助属性类别_fname
,辅助属性类别_fnumber
,物料属性_fname
,默认仓库_fname
,默认仓库_fnumber
,来源_fname
,id)values(NEW.*);
elseif new.id >= 900001 then
insert into TEXT11(
代码
,名称
,物料全名
,规格型号
,辅助属性类别_fname
,辅助属性类别_fnumber
,物料属性_fname
,默认仓库_fname
,默认仓库_fnumber
,来源_fname
,id)values(NEW.*);
else
raise notice '数据未插入:%',new.id ;
end if;
return null;
end;
$$language plpgsql ;
--创建触发器
create trigger text_tgr before insert on text_import for each row execute procedure ins_tg();
--继承式分区造数据
insert into public.text_import
select
md5((random()*random())::text ),
md5((random()*random()) ::text),
md5((random()*random())::text ),
md5((random()*random()) ::text),
md5((random()*random()) ::text),
md5((random()*random())::text ),
md5((random()*random())::text ),
md5((random()*random())::text ),
md5((random()*random()) ::text),
md5((random()*random()) ::text),
n
FROM
generate_series(1,3000000) as n;
此时每一个表中都会有一个check约束。在创建子表的时候也可以不增加约束条件的创建。但是要确保被触发的函数内部的判断条件无误。
alter table public.text11 no inherit public.text_import ; --解除继承关系
alter table public.text11 inherit public.text_import ; --绑定继承关系
--也可以采用创建分区表的语法增加子表
CREATE TABLE public.text11 (
CONSTRAINT text11_id_check CHECK ((id >= 1000000))
)
INHERITS (public.text_import);
--在继承式分区表增加子表的时候,需要同步修改触发的函数其内容
--列值分区
drop table if exists main.extract_train CASCADE;
create table main.extract_train (
sec_code int not null,
remark varchar(9999) not null,
remark_type varchar(999),
sec_name varchar(100)
) partition by list (remark_type);
-- 创建分区表
CREATE TABLE main.extract_train_bnlz PARTITION OF main.extract_train FOR VALUES IN ('不能履职');
CREATE TABLE main.extract_train_cwzj PARTITION OF main.extract_train FOR VALUES IN ('财务造假');
CREATE TABLE main.extract_train_cpwk PARTITION OF main.extract_train FOR VALUES IN ('产品违规');
CREATE TABLE main.extract_train_ggfm PARTITION OF main.extract_train FOR VALUES IN ('高管负面');
CREATE TABLE main.extract_train_gsyd PARTITION OF main.extract_train FOR VALUES IN ('公司股市异常');
CREATE TABLE main.extract_train_jywg PARTITION OF main.extract_train FOR VALUES IN ('交易违规');
CREATE TABLE main.extract_train_pjtz PARTITION OF main.extract_train FOR VALUES IN ('评级调整');
CREATE TABLE main.extract_train_other PARTITION OF main.extract_train FOR VALUES IN ('其他');
CREATE TABLE main.extract_train_sxcx PARTITION OF main.extract_train FOR VALUES IN ('涉嫌传销');
CREATE TABLE main.extract_train_ffjz PARTITION OF main.extract_train FOR VALUES IN ('涉嫌非法集资');
CREATE TABLE main.extract_train_sxqz PARTITION OF main.extract_train FOR VALUES IN ('涉嫌欺诈');
CREATE TABLE main.extract_train_sxwf PARTITION OF main.extract_train FOR VALUES IN ('涉嫌违法');
CREATE TABLE main.extract_train_slpl PARTITION OF main.extract_train FOR VALUES IN ('失联跑路');
CREATE TABLE main.extract_train_gdbg PARTITION OF main.extract_train FOR VALUES IN ('实控人股东变更');
CREATE TABLE main.extract_train_txkn PARTITION OF main.extract_train FOR VALUES IN ('提现困难');
CREATE TABLE main.extract_train_tswq PARTITION OF main.extract_train FOR VALUES IN ('投诉维权');
CREATE TABLE main.extract_train_xyty PARTITION OF main.extract_train FOR VALUES IN ('歇业停业');
CREATE TABLE main.extract_train_xpwk PARTITION OF main.extract_train FOR VALUES IN ('信批违规');
CREATE TABLE main.extract_train_yjxh PARTITION OF main.extract_train FOR VALUES IN ('业绩下滑');
CREATE TABLE main.extract_train_czsb PARTITION OF main.extract_train FOR VALUES IN ('重组失败');
CREATE TABLE main.extract_train_zcfm PARTITION OF main.extract_train FOR VALUES IN ('资产负面');
CREATE TABLE main.extract_train_zhfx PARTITION OF main.extract_train FOR VALUES IN ('资金账户风险');
CREATE TABLE main.extract_train_default PARTITION OF main.extract_train DEFAULT;
列值分区常常见于一些大表,而某一字段仅有一些固定的值,类似商品种类,商品等级,省市等,相较于继承式分区,也更有优势,创建起来也更简便。
当然这个分区键的列值在某一个时间突然有增加或者删减的时候,添加起来也很方便
--解除子表
alter table main.extract_train detach partition main.extract_train_default ;
--解除子表
alter table main.extract_train detach partition main.extract_train_zhfx ;
--增加子表 (分区键值为'资金账户风险)
alter table main.extract_train attach partition main.extract_train_zhfx for values in ('资金账户风险') ;
--增加子表 (分区键值为 other)
alter table main.extract_train attach partition main.extract_train_default default ;
--可以采用创建分区表的方法增加分区表
--解除子表
alter table main.extract_train detach partition main.extract_train_zhfx ;
drop table if exists main.extract_train_zhfx ;
CREATE TABLE main.extract_train_zhfx PARTITION OF main.extract_train FOR VALUES IN ('资金账户风险');
--解除子表
alter table main.extract_train detach partition main.extract_train_default ;
drop table if exists main.extract_train_default;
create table main.extract_train_default partition of main.extract_train default ;
drop table if exists pg_partition_text cascade;
--声明式分区
CREATE TABLE pg_partition_text (
id SERIAL,
remark varchar(200) not null,
partition_date date,
load_time date default now()
) PARTITION BY RANGE (partition_date);
--创建子表
create table pkslow_person_r0 partition of pg_partition_text for values from ('20200101') to ('20210101');
create table pkslow_person_r1 partition of pg_partition_text for values from ('20210101') to ('20220101') ;
create table pkslow_person_r2 partition of pg_partition_text for values from ('20220101') to('20230101') ;
create table pkslow_person_r3 partition of pg_partition_text for values from ('20230101') to ('20240101') ;
此时创建的分区子表分区键的值范围 是左闭右开的状态 for values from ('20200101') to ('20210101'); 相当于 partition_values >= ('20200101') and partition_values < ('20210101')
的状态,
等价于['20200101','20210101')
插入测试数据
insert into pg_partition_text (remark,partition_date) select md5(n::text),n from generate_series(date('20200101'),current_date,interval '1 month') n
--解除子表
alter table pg_partition_text detach partition pkslow_person_r0 ;
--增加子表 (分区键值为'资金账户风险)
alter table pg_partition_text attach partition pkslow_person_r0 for values from ('20200101') to ('20210101');
--也可以采用创建分区表的方法增加分区表
--解除子表
drop table if exists pkslow_person_r0 ;
create table pkslow_person_r0 partition of pg_partition_text for values from ('20200101') to ('20210101');
相对于继承式分区,声明式分区的使用更为灵活,性能也会更加,但是列值分区,和范围分区其分区键的值在一个固定范围中时 使用就会比较方便,无需增减分区表,修订分区区间范围。如果没有满足这些的条件时,可以使用哈希分区。采用哈希分区,数据库会调用内置的pg_partition_hash函数进行计算其插入数据该字段的哈希值 根据哈希值取余数,由于余数的不同而进入到对应的分区子表中。
注意:pg_partition_hash
函数是内部函数,不允许直接调用。它仅用于在内部计算哈希分区键的哈希值。
-- 创建主表
create table pkslow_person_h (
age int not null,
city varchar not null,
seq bigserial
) partition by hash (city);
-- 创建分区表
create table pkslow_person_h1 partition of pkslow_person_h for values with (modulus 4, remainder 0);
create table pkslow_person_h2 partition of pkslow_person_h for values with (modulus 4, remainder 1);
create table pkslow_person_h3 partition of pkslow_person_h for values with (modulus 4, remainder 2);
create table pkslow_person_h4 partition of pkslow_person_h for values with (modulus 4, remainder 3);
-- 插入测试数据
insert into pkslow_person_h(age, city) VALUES (1, 'GZ');
insert into pkslow_person_h(age, city) VALUES (2, 'SZ');
insert into pkslow_person_h(age, city) VALUES (21, 'SZ');
insert into pkslow_person_h(age, city) VALUES (13, 'BJ');
insert into pkslow_person_h(age, city) VALUES (43, 'SH');
insert into pkslow_person_h(age, city) VALUES (28, 'HK');
这个在哈希分区表中并不常用,因为在创建的过程中,余数可能有的情况,再未来也不可能发生变化,所以也不需要增加分区表。
--解除分区表
alter table pkslow_person_h detach partition pkslow_person_h1;
--增加分区表
alter table pkslow_person_h attach partition pkslow_person_h1 for values with (modulus 4, remainder 0);
--也可以使用创建分区表的方法再次绑定分区父表
--解除分区表
alter table pkslow_person_h detach partition pkslow_person_h1;
drop table id exist pkslow_person_h1 ;
create table pkslow_person_h1 partition of pkslow_person_h for values with (modulus 4, remainder 0);
这里的分区键是city,其值也是字符类型,postgresql数据库会根据内置的pg_partition_hash函数进行调用,计算其插入数据该字段的哈希值 根据哈希值取余数,由于余数的不同而进入到对应的分区子表中
drop table text_import cascade
cascade关键字危险性极高,虽然它可以一次性删除继承式分区 的子表 触发器和函数 但是在实际生产中极有可能在未知的情况有被挂接的相关表或者object。
drop table 父表名称
删除声明式分区无需添加cascade关键字,父表被删除后子表也会对应的数据、约束和索引被删除掉,