PostgreSQL之 本地分割槽表的用法和優化
資料分割槽的好處
1)分割槽後,單個分割槽表的索引和表都變小了,可以保持在記憶體裡面,適合把熱資料從大表拆分出來的場景。
2)對於大範圍的查詢,大表可以通過索引來避免全表掃描。但是如果分割槽了的話,可以使用分割槽的全表掃描。適合經常要做大範圍掃描的場景,按照範圍分割槽(分割槽後採用全表掃描),減少索引帶來的隨機BLOCK掃描。
3)大批量的資料匯入或刪除,對於大表來說,刪除大量的資料使用DELETE的話會帶來大量的VACUUM操作負擔。而使用分割槽表的話可以直接DROP分割槽,或者脫離子表和父表的繼承關係。
4)使用分割槽表,還有一個好處是,可以把不常用的分割槽放到便宜的儲存上。
5)因為每個表只能放在一個表空間上,表空間和目錄對應,表的大小受到表空間大小的限制,使用分割槽表則更加靈活。
資料庫分割槽表舉例
1)範圍分割槽
根據欄位儲存的值的取值範圍進行分割槽,例如日誌表的時間欄位,使用者表的ID範圍等等。
2)雜湊分割槽
根據欄位儲存值HASH和分割槽數做位元運算得到一個唯一的分割槽ID。(對於版本的升級、軟體的升級一定要確保雜湊演算法的一致)
或者取模也行
例如mod(hashtext(name),16),對16個分割槽的場景
3)list分割槽
與雜湊分割槽類似,但是直接使用欄位值作為分割槽條件。適合KEY值比較少並且比較均勻的場景。
例如按性別欄位作為分割槽欄位,那麼就分成了2個區。
分割槽和表繼承的概念
繼承表自動繼承父表的約束,非空約束。
但是不自動繼承的是(uk,pk,fk,索引,儲存引數等)。
示例:
postgres=# create table p1(id int primary key, info text unique, c1 int check(c1>0), c2 int not null, c3 int unique);
CREATE TABLE
postgres=# create table c1(like p1) inherits(p1);
NOTICE: merging column "id" with inherited definition
NOTICE: merging column "info" with inherited definition
NOTICE: merging column "c1" with inherited definition
NOTICE: merging column "c2" with inherited definition
NOTICE: merging column "c3" with inherited definition
CREATE TABLE
postgres=# \d+ p1
Table "public.p1"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
------------+---------+-------------+-----------+----------+-------------+----------------+-------------
id | integer | | not null | | plain | |
info | text | | | | extended | |
c1 | integer | | | | plain | |
c2 | integer | | not null | | plain | |
c3 | integer | | | | plain | |
Indexes:
"p1_pkey" PRIMARY KEY, btree (id)
"p1_c3_key" UNIQUE CONSTRAINT, btree (c3)
"p1_info_key" UNIQUE CONSTRAINT, btree (info)
Check constraints:
"p1_c1_check" CHECK (c1 > 0)
Child tables: c1
子表自動繼承了父表的非空約束,自定義約束。未自動繼承PK和UK。
postgres=# \d+ c1
Table "public.c1"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+----------+-------------+-----------+----------+------------+----------------+-------------
id | integer | | not null | | plain | |
info | text | | | | extended | |
c1 | integer | | | | plain | |
c2 | integer | | not null | | plain | |
c3 | integer | | | | plain | |
Check constraints:
"p1_c1_check" CHECK (c1 > 0)
Inherits: p1
如果要繼承UK,PK,索引,儲存結構等,建立時加including all;
postgres=# drop table c1;
DROP TABLE
postgres=# create table c1(like p1 including all) inherits(p1);
NOTICE: merging column "id" with inherited definition
NOTICE: merging column "info" with inherited definition
NOTICE: merging column "c1" with inherited definition
NOTICE: merging column "c2" with inherited definition
NOTICE: merging column "c3" with inherited definition
NOTICE: merging constraint "p1_c1_check" with inherited definition
CREATE TABLE
postgres=# \d c1
Table "public.c1"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | not null |
info | text | | |
c1 | integer | | |
c2 | integer | | not null |
c3 | integer | | |
Indexes:
"c1_pkey" PRIMARY KEY, btree (id)
"c1_c3_key" UNIQUE CONSTRAINT, btree (c3)
"c1_info_key" UNIQUE CONSTRAINT, btree (info)
Check constraints:
"p1_c1_check" CHECK (c1 > 0)
Inherits: p1
可以使用alter table解除和加入繼承關係:
postgres=# alter table c1 no inherit p1;
ALTER TABLE
postgres=# alter table c1 drop constraint p1_c1_check;
ALTER TABLE
當主表和繼承表的欄位個數,順序,名字,以及型別或者約束條件有任何不一致時,就無法自動新增繼承。
這裡指的是預設繼承的約束,非空約束等。不包含不會預設繼承的約束UK,PK,FK等。
postgres=# alter table c1 inherit p1;
ERROR: child table is missing constraint "p1_c1_check"
加上約束後就可以了:
postgres=# alter table c1 add constraint p1_c1_check check(c1>0);
ALTER TABLE
postgres=# alter table c1 inherit p1;
ALTER TABLE
一個表可以同時繼承多個父表,一個父表可以被多個子表繼承。
但是必須注意,一個表繼承了多個主表的情況,共有欄位上,所有的父表的約束包括not null的定義都必須繼承過來(同樣不包括pk,uk,fk等)。
查主表預設情況下是會連帶查詢所有的子表的,包括更深的子表(子表的子表)。
例如select * from p1;預設會查詢所有子表和自身。
postgres=# explain select * from p1;
QUERY PLAN
-------------------------------------------------------------
Append (cost=0.00..20.70 rows=1071 width=48)
-> Seq Scan on p1 (cost=0.00..0.00 rows=1 width=48)
-> Seq Scan on c1 (cost=0.00..20.70 rows=1070 width=48)
(3 rows)
除非使用only或者修改sql_inheritance(預設為on,pg10已經刪除此配置選項,從預設值更改此設定會導致引用父表的查詢不包括子表。然而,SQL標準要求它們被包含在內)。
postgres=# explain select * from only p1;
QUERY PLAN
---------------------------------------------------
Seq Scan on p1 (cost=0.00..0.00 rows=1 width=48)
(1 row)
SELECT,UPDATE,DELETE,TRUNCATE,DROP命令在主表上操作預設都會影響到子表。
這些操作必須注意了,小心謹慎。
INSERT和COPY命令,這兩條命令只是對當前表操作的,不會擴充套件到子表。
插入到指定分割槽的實現,使用觸發器(由於insert、copy插入到父表,跟子表沒有關係)
postgres=# create table p(id int, info text, crt_time timestamp);
CREATE TABLE
postgres=# create table c1(like p) inherits(p);
NOTICE: merging column "id" with inherited definition
NOTICE: merging column "info" with inherited definition
NOTICE: merging column "crt_time" with inherited definition
CREATE TABLE
postgres=# create table c2(like p) inherits(p);
NOTICE: merging column "id" with inherited definition
NOTICE: merging column "info" with inherited definition
NOTICE: merging column "crt_time" with inherited definition
CREATE TABLE
postgres=# create table c3(like p) inherits(p);
NOTICE: merging column "id" with inherited definition
NOTICE: merging column "info" with inherited definition
NOTICE: merging column "crt_time" with inherited definition
CREATE TABLE
postgres=# create table c4(like p) inherits(p);
NOTICE: merging column "id" with inherited definition
NOTICE: merging column "info" with inherited definition
NOTICE: merging column "crt_time" with inherited definition
CREATE TABLE
postgres=# alter table c1 add constraint ck check (crt_time>='2018-04-04' and crt_time<'2018-05-04’);
ALTER TABLE
postgres=# alter table c2 add constraint ck check (crt_time>='2018-05-04' and crt_time<'2018-06-04’);
ALTER TABLE
postgres=# alter table c3 add constraint ck check (crt_time>='2018-06-04' and crt_time<'2018-07-04’);
ALTER TABLE
postgres=# alter table c4 add constraint ck check (crt_time>='2018-07-04' and crt_time<'2018-08-04’);
ALTER TABLE
postgres=# create or replace function ins_tg() returns trigger as $$
declare
begin
if NEW.crt_time>='2018-04-04'and NEW.crt_time<'2018-05-04' then
insert into c1(id, info, crt_time) values(NEW.*);
elseif NEW.crt_time>='2018-05-04' and NEW.crt_time<'2018-06-04' then
insert into c2(id, info, crt_time) values(NEW.*);
elseif NEW.crt_time>='2018-06-04' and NEW.crt_time<'2018-07-04' then
insert into c3(id, info, crt_time) values(NEW.*);
elseif NEW.crt_time>='2018-07-04' and NEW.crt_time<'2018-08-04' then
insert into c4(id, info, crt_time) values(NEW.*);
else
raise exception 'crt_time overflow.';
end if;
return null;
end;
$$language plpgsql strict;
CREATE FUNCTION
postgres=# create trigger tg1 before insert on p for each row execute procedure ins_tg();
CREATE TRIGGER
postgres=# insert into p(id, info, crt_time) values(1, 'test', now());
INSERT 0 0
postgres=# select * from c1;
id | info | crt_time
----+------+----------
(0 rows)
postgres=# select * from c2;
id | info | crt_time
----+------+----------------------------
1 | test | 2018-05-17 10:48:44.586036
(1 row)
postgres=# select * from c3;
id | info | crt_time
----+------+----------
(0 rows)
postgres=# select * from c4;
id | info | crt_time
----+------+----------
(0 rows)
postgres=# select * from only p;
id | info | crt_time
----+------+----------
(0 rows)
postgres=# insert into p(id, info, crt_time) values(1, 'test', '2018-06-04');
INSERT 0 0
postgres=# select * from c2;
id | info | crt_time
----+------+----------------------------
1 | test | 2018-05-17 10:48:44.586036
(1 row)
by boluo