pg10之后有内置分区表,相对于之前传统分区表更好用
---传统分区表
--继承表
postgres=# create table tbl_log(id int4,create_date date,log_type text); CREATE TABLE
创建一张子表
postgres=# create table tbl_log_sql (sql text ) inherits(tbl_log); CREATE TABLE
父表子表都可以插入数据,查看表结构
postgres=# \d+ tbl_log Table "public.tbl_log" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description -------------+---------+-----------+----------+---------+----------+--------------+------------- id | integer | | | | plain | | create_date | date | | | | plain | | log_type | text | | | | extended | | Child tables: tbl_log_sql postgres=# \d tbl_log_sql Table "public.tbl_log_sql" Column | Type | Collation | Nullable | Default -------------+---------+-----------+----------+--------- id | integer | | | create_date | date | | | log_type | text | | | sql | text | | | Inherits: tbl_log
查看数据 postgres=# insert into tbl_log values (1,'2021-10-19',null); INSERT 0 1 postgres=# insert into tbl_log_sql values (1,'2021-10-18',null,'select 2'); INSERT 0 1 postgres=# select * from tbl_log; id | create_date | log_type ----+-------------+---------- 1 | 2021-10-19 | 1 | 2021-10-18 | (2 rows) postgres=# select * from tbl_log_sql; id | create_date | log_type | sql ----+-------------+----------+---------- 1 | 2021-10-18 | | select 2 (1 row)
通过pg_class确认哪张表
postgres=# select p.relname,p.oid,c.* from tbl_log c,pg_class p where c.tableoid=p.oid; relname | oid | id | create_date | log_type -------------+-------+----+-------------+---------- tbl_log | 49173 | 1 | 2021-10-19 | tbl_log_sql | 49179 | 1 | 2021-10-18 |
只查询父表数据,需要在父表名称前加关键字only
postgres=# select * from only tbl_log; id | create_date | log_type ----+-------------+---------- 1 | 2021-10-19 |
如果没有加only会对父表和所有子表进行操作
postgres=# delete from tbl_log; DELETE 2 postgres=# select * from tbl_log; id | create_date | log_type ----+-------------+---------- (0 rows)
创建传统分区表
注意:
创建父表
postgres=# create table log_ins(id serial,user_id int4,create_time timestamp(0) without time zone); CREATE TABLE
创建子表以及给子表创建索引,父表不存储数据,可以不用在父表上创建
postgres=# create table log_ins_history(CHECK (create_time < '2017-01-01')) inherits(log_ins); CREATE TABLE postgres=# create table log_ins_201701(CHECK (create_time >= '2017-01-01' and create_time < '2017-02-01')) inherits(log_ins); CREATE TABLE postgres=# create table log_ins_201702(CHECK (create_time >= '2017-02-01' and create_time < '2017-03-01')) inherits(log_ins); CREATE TABLE postgres=# create table log_ins_201703(CHECK (create_time >= '2017-03-01' and create_time < '2017-04-01')) inherits(log_ins); CREATE TABLE postgres=# create table log_ins_201704(CHECK (create_time >= '2017-04-01' and create_time < '2017-05-01')) inherits(log_ins); CREATE TABLE postgres=# create table log_ins_201705(CHECK (create_time >= '2017-05-01' and create_time < '2017-06-01')) inherits(log_ins); CREATE TABLE postgres=# create table log_ins_201706(CHECK (create_time >= '2017-06-01' and create_time < '2017-07-01')) inherits(log_ins); CREATE TABLE postgres=# create table log_ins_201707(CHECK (create_time >= '2017-07-01' and create_time < '2017-08-01')) inherits(log_ins); CREATE TABLE postgres=# create table log_ins_201708(CHECK (create_time >= '2017-08-01' and create_time < '2017-09-01')) inherits(log_ins); CREATE TABLE postgres=# create table log_ins_201709(CHECK (create_time >= '2017-09-01' and create_time < '2017-10-01')) inherits(log_ins); CREATE TABLE postgres=# create table log_ins_201710(CHECK (create_time >= '2017-10-01' and create_time < '2017-11-01')) inherits(log_ins); CREATE TABLE postgres=# create table log_ins_201711(CHECK (create_time >= '2017-11-01' and create_time < '2017-12-01')) inherits(log_ins); CREATE TABLE postgres=# create table log_ins_201712(CHECK (create_time >= '2017-12-01' and create_time < '2018-01-01')) inherits(log_ins); CREATE TABLE
postgres=# create index idx_his_ctime on log_ins_history using btree (create_time); CREATE INDEX postgres=# create index idx_log_ins_201701_ctime on log_ins_201701 using btree (create_time); CREATE INDEX postgres=# create index idx_log_ins_201702_ctime on log_ins_201702 using btree (create_time); CREATE INDEX postgres=# create index idx_log_ins_201703_ctime on log_ins_201703 using btree (create_time); CREATE INDEX postgres=# create index idx_log_ins_201704_ctime on log_ins_201704 using btree (create_time); CREATE INDEX postgres=# create index idx_log_ins_201705_ctime on log_ins_201705 using btree (create_time); CREATE INDEX postgres=# create index idx_log_ins_201706_ctime on log_ins_201706 using btree (create_time); CREATE INDEX postgres=# create index idx_log_ins_201707_ctime on log_ins_201707 using btree (create_time); CREATE INDEX postgres=# create index idx_log_ins_201708_ctime on log_ins_201708 using btree (create_time); CREATE INDEX postgres=# create index idx_log_ins_201709_ctime on log_ins_201709 using btree (create_time); CREATE INDEX postgres=# create index idx_log_ins_201710_ctime on log_ins_201710 using btree (create_time); CREATE INDEX postgres=# create index idx_log_ins_201711_ctime on log_ins_201711 using btree (create_time); CREATE INDEX postgres=# create index idx_log_ins_201712_ctime on log_ins_201712 using btree (create_time); CREATE INDEX
创建触发器函数设置数据插入父表时的路由规则
create or replace function log_ins_insert_trigger() returns trigger language plpgsql AS $function$ begin if (NEW.create_time < '2017-01-01') THEN insert into log_ins_history values (NEW.*); elsif (NEW.create_time >= '2017-01-01' and NEW.create_time<'2017-02-01') THEN insert into log_ins_201701 values (NEW.*); elsif (NEW.create_time >= '2017-02-01' and NEW.create_time<'2017-03-01') THEN insert into log_ins_201702 values (NEW.*); elsif (NEW.create_time >= '2017-03-01' and NEW.create_time<'2017-04-01') THEN insert into log_ins_201703 values (NEW.*); elsif (NEW.create_time >= '2017-04-01' and NEW.create_time<'2017-05-01') THEN insert into log_ins_201704 values (NEW.*); elsif (NEW.create_time >= '2017-05-01' and NEW.create_time<'2017-06-01') THEN insert into log_ins_201705 values (NEW.*); elsif (NEW.create_time >= '2017-06-01' and NEW.create_time<'2017-07-01') THEN insert into log_ins_201706 values (NEW.*); elsif (NEW.create_time >= '2017-07-01' and NEW.create_time<'2017-08-01') THEN insert into log_ins_201707 values (NEW.*); elsif (NEW.create_time >= '2017-08-01' and NEW.create_time<'2017-09-01') THEN insert into log_ins_201708 values (NEW.*); elsif (NEW.create_time >= '2017-09-01' and NEW.create_time<'2017-10-01') THEN insert into log_ins_201709 values (NEW.*); elsif (NEW.create_time >= '2017-10-01' and NEW.create_time<'2017-11-01') THEN insert into log_ins_201710 values (NEW.*); elsif (NEW.create_time >= '2017-11-01' and NEW.create_time<'2017-12-01') THEN insert into log_ins_201711 values (NEW.*); elsif (NEW.create_time >= '2017-12-01' and NEW.create_time<'2018-01-01') THEN insert into log_ins_201712 values (NEW.*); else raise exception 'create_time out of range. fix the log_ins_insert_tigger() function!'; END if; return null; end; $function$;
函数中的NEW.*是要指向插入的数据行,在父表上定义插入触发器
postgres=# create trigger insert_log_ins_trigger before insert on log_ins for each row execute procedure log_ins_insert_trigger(); CREATE TRIGGER
触发器知识:
使用分区表
插入数据
postgres=# insert into log_ins(user_id,create_time) select round(100000000*random()),generate_series('2016-12-01'::date,'2017-12-01'::date, '1 minute');; INSERT 0 0
查看父表数据,子表数据
postgres=# select * from only log_ins limit 2; id | user_id | create_time ----+---------+------------- (0 rows) postgres=# select * from log_ins_201703 limit 2; id | user_id | create_time --------+----------+--------------------- 129601 | 50525906 | 2017-03-01 00:00:00 129602 | 6842102 | 2017-03-01 00:01:00 (2 rows)
查询父表还是子表,假如检索2017-01-01这一天的数据,查询父表和子表之间的差异
postgres=# explain analyze select * from log_ins where create_time > '2017-01-01' and create_time <'2017-01-02'; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------------------------- Append (cost=0.00..62.21 rows=1497 width=16) (actual time=0.009..0.166 rows=1439 loops=1) -> Seq Scan on log_ins (cost=0.00..0.00 rows=1 width=16) (actual time=0.001..0.001 rows=0 loops=1) Filter: ((create_time > '2017-01-01 00:00:00'::timestamp without time zone) AND (create_time < '2017-01-02 00:00:00'::timestamp without time zone)) -> Index Scan using idx_log_ins_201701_ctime on log_ins_201701 (cost=0.29..62.21 rows=1496 width=16) (actual time=0.007..0.111 rows=1439 loops=1) Index Cond: ((create_time > '2017-01-01 00:00:00'::timestamp without time zone) AND (create_time < '2017-01-02 00:00:00'::timestamp without time zone)) Planning time: 0.270 ms Execution time: 0.224 ms (7 rows) postgres=# explain analyze select * from log_ins_201701 where create_time > '2017-01-01' and create_time <'2017-01-02'; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------------------- Index Scan using idx_log_ins_201701_ctime on log_ins_201701 (cost=0.29..62.21 rows=1496 width=16) (actual time=0.009..0.144 rows=1439 loops=1) Index Cond: ((create_time > '2017-01-01 00:00:00'::timestamp without time zone) AND (create_time < '2017-01-02 00:00:00'::timestamp without time zone)) Planning time: 0.072 ms Execution time: 0.191 ms (4 rows)
可以看出直接访问子表性能有提升,并发量上去的话效果更明显
constraint_exclusion参数
postgres=# show constraint_exclusion; constraint_exclusion ---------------------- partition
on:所有表都通过约束优化查询;
off:所有表不通过约束优化查询;
partition:只对继承表和union all子查询通过检索约束来优化查询(建议)
添加分区,严格按照以下步骤
--创建分区
postgres=# create table log_ins_201801(like log_ins including all); CREATE TABLE
--添加约束
postgres=# alter table log_ins_201801 add constraint log_ins_201801_create_time_check CHECK(create_time >='2018-01-01' and create_time<'2018-02-01'); ALTER TABLE
--刷新触发器函数
postgres=# create or replace function log_ins_insert_trigger() postgres-# returns trigger postgres-# language plpgsql postgres-# AS $function$ postgres$# begin postgres$# if (NEW.create_time < '2017-01-01') THEN postgres$# insert into log_ins_history values (NEW.*); postgres$# elsif (NEW.create_time >= '2017-01-01' and NEW.create_time<'2017-02-01') THEN insert into log_ins_201701 values (NEW.*); postgres$# elsif (NEW.create_time >= '2017-02-01' and NEW.create_time<'2017-03-01') THEN insert into log_ins_201702 values (NEW.*); postgres$# elsif (NEW.create_time >= '2017-03-01' and NEW.create_time<'2017-04-01') THEN insert into log_ins_201703 values (NEW.*); postgres$# elsif (NEW.create_time >= '2017-04-01' and NEW.create_time<'2017-05-01') THEN insert into log_ins_201704 values (NEW.*); postgres$# elsif (NEW.create_time >= '2017-05-01' and NEW.create_time<'2017-06-01') THEN insert into log_ins_201705 values (NEW.*); postgres$# elsif (NEW.create_time >= '2017-06-01' and NEW.create_time<'2017-07-01') THEN insert into log_ins_201706 values (NEW.*); postgres$# elsif (NEW.create_time >= '2017-07-01' and NEW.create_time<'2017-08-01') THEN insert into log_ins_201707 values (NEW.*); postgres$# elsif (NEW.create_time >= '2017-08-01' and NEW.create_time<'2017-09-01') THEN insert into log_ins_201708 values (NEW.*); postgres$# elsif (NEW.create_time >= '2017-09-01' and NEW.create_time<'2017-10-01') THEN insert into log_ins_201709 values (NEW.*); postgres$# elsif (NEW.create_time >= '2017-10-01' and NEW.create_time<'2017-11-01') THEN insert into log_ins_201710 values (NEW.*); postgres$# elsif (NEW.create_time >= '2017-11-01' and NEW.create_time<'2017-12-01') THEN insert into log_ins_201711 values (NEW.*); postgres$# elsif (NEW.create_time >= '2017-12-01' and NEW.create_time<'2018-01-01') THEN insert into log_ins_201712 values (NEW.*); postgres$# elsif (NEW.create_time >= '2018-01-01' and NEW.create_time<'2018-02-01') THEN insert into log_ins_201801 values (NEW.*); postgres$# else postgres$# raise exception 'create_time out of range. fix the log_ins_insert_tigger() function!'; postgres$# END if; postgres$# return null; postgres$# end; postgres$# $function$;
--所有步骤完成之后,将新分区log_ins_201801继承到父表log_ins
postgres=# alter table log_ins_201801 inherit log_ins; ALTER TABLE