postgresql之分區表


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)

 創建傳統分區表

注意:

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM