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)
創建傳統分區表
注意:
- 創建父表,如果父表定義約束,子表也會受到約束,因此除非是全局約束,否則不應該創建在父表上,另外父表不應該寫入數據。
- 用inherits的方式創建繼承表,稱之為子表或分區,子表的字段應該與父表保持一致。
- 給所有子表創建約束,只有約束條件的數據才能寫入對應分區或子表,注意:分區約束不能有重疊。
- 給所有子表創建索引,由於繼承操作不會繼承父表上的索引,因此需要手工創建
- 在父表上定義觸發器insert,update,delete,將sql分發到對應分區(可選),從應用方可以根據分區處理
- 啟用consratint_exclusion參數,如果設置成off,則會父表上的sql性能會降低
創建父表
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
