1. 程式人生 > >PostgreSQL 分割槽表, pg_pathman ,PostgreSQL 10介紹及效能對比(轉載)

PostgreSQL 分割槽表, pg_pathman ,PostgreSQL 10介紹及效能對比(轉載)

PostgreSQL 分割槽表, pg_pathman ,PostgreSQL 10介紹及效能對比 原
yonj1e yonj1e 釋出於 2017/03/27 15:23 字數 5231 閱讀 851 收藏 2 點贊 0 評論 0
PostgreSQL
簡介

在資料庫日漸龐大的今天,為了方便對資料庫資料的管理,比如按時間,按地區去統計一些資料時,基數過於龐大,多有不便。很多商業資料庫都提供分割槽的概念,按不同的維度去存放資料,便於後期的管理,PostgreSQL也不例外。

PostgresSQL分割槽的意思是把邏輯上的一個大表分割成物理上的幾塊兒。分割槽不僅能帶來訪問速度的提升,關鍵的是,它能帶來管理和維護上的方便。

分割槽的具體好處是:

改善查詢效能:對分割槽物件的查詢可以僅搜尋自己關心的分割槽,提高檢索速度
增強可用性:如果表的某個分割槽出現故障,表在其他分割槽的資料仍然可用
維護方便:如果表的某個分割槽出現故障,需要修復資料,只修復該分割槽即可
均衡I/O:可以把不同的分割槽對映到不同磁碟以平衡I/O,改善整個系統性能
表分割槽介紹

資料庫表分割槽把一個大的物理表分成若干個小的物理表,並使得這些小物理表在邏輯上可以被當成一張表來使用。

主表 / 父表 / Master Table 該表是建立子表的模板。它是一個正常的普通表,但正常情況下它並不儲存任何資料。
子表 / 分割槽表 / Child Table / Partition Table 這些表繼承並屬於一個主表。子表中儲存所有的資料。主表與分割槽表屬於一對多的關係,也就是說,一個主表包含多個分割槽表,而一個分割槽表只從屬於一個主表
傳統分割槽表

現在PostgreSQL支援通過表繼承來實現表的分割槽。父表是普通表並且正常情況下並不儲存任何資料,它的存在只是為了代表整個資料集。PostgreSQL可實現如下兩種表分割槽

範圍分割槽 每個分割槽表包含一個或多個欄位組合的一部分,並且每個分割槽表的範圍互不重疊。比如可近日期範圍分割槽
列表分割槽 分割槽表顯示列出其所包含的key值
實現分割槽

1,建立"主表",所有分割槽都從它繼承。

這個表中沒有資料,不要在這個表上定義任何檢查約束,除非你希望約束同樣也適用於所有分割槽。同樣,在其上定義任何索引或者唯一約束也沒有意義。

CREATE TABLE measurement (
city_id int not null,
logdate date not null,
peaktemp int,
unitsales int
);
2,建立幾個"子表",每個都從主表上繼承。通常,這些表不會增加任何欄位。

我們將把子表稱作分割槽,儘管它們就是普通的PostgreSQL表。

CREATE TABLE measurement_y2017m01 ( ) INHERITS (measurement);
CREATE TABLE measurement_y2017m02 ( ) INHERITS (measurement);
CREATE TABLE measurement_y2017m03 ( ) INHERITS (measurement);
CREATE TABLE measurement_y2017m04 ( ) INHERITS (measurement);
CREATE TABLE measurement_y2017m05 ( ) INHERITS (measurement);
CREATE TABLE measurement_y2017m06 ( ) INHERITS (measurement);
CREATE TABLE measurement_y2017m07 ( ) INHERITS (measurement);
CREATE TABLE measurement_y2017m08 ( ) INHERITS (measurement);
CREATE TABLE measurement_y2017m09 ( ) INHERITS (measurement);
CREATE TABLE measurement_y2017m10 ( ) INHERITS (measurement);
CREATE TABLE measurement_y2017m11 ( ) INHERITS (measurement);
CREATE TABLE measurement_y2017m12 ( ) INHERITS (measurement);
3,給分割槽表增加約束,定義每個分割槽允許的健值。

確保這些約束能夠保證在不同的分割槽裡不會有重疊的鍵值。

alter table measurement_y2017m01 add CHECK ( logdate >= DATE ‘2017-01-01’ AND logdate < DATE ‘2017-02-01’ );
alter table measurement_y2017m02 add CHECK ( logdate >= DATE ‘2017-02-01’ AND logdate < DATE ‘2017-03-01’ );
alter table measurement_y2017m03 add CHECK ( logdate >= DATE ‘2017-03-01’ AND logdate < DATE ‘2017-04-01’ );
alter table measurement_y2017m04 add CHECK ( logdate >= DATE ‘2017-04-01’ AND logdate < DATE ‘2017-05-01’ );
alter table measurement_y2017m05 add CHECK ( logdate >= DATE ‘2017-05-01’ AND logdate < DATE ‘2017-06-01’ );
alter table measurement_y2017m06 add CHECK ( logdate >= DATE ‘2017-06-01’ AND logdate < DATE ‘2017-07-01’ );
alter table measurement_y2017m07 add CHECK ( logdate >= DATE ‘2017-07-01’ AND logdate < DATE ‘2017-08-01’ );
alter table measurement_y2017m08 add CHECK ( logdate >= DATE ‘2017-08-01’ AND logdate < DATE ‘2017-09-01’ );
alter table measurement_y2017m09 add CHECK ( logdate >= DATE ‘2017-09-01’ AND logdate < DATE ‘2017-10-01’ );
alter table measurement_y2017m10 add CHECK ( logdate >= DATE ‘2017-10-01’ AND logdate < DATE ‘2017-11-01’ );
alter table measurement_y2017m11 add CHECK ( logdate >= DATE ‘2017-11-01’ AND logdate < DATE ‘2017-12-01’ );
alter table measurement_y2017m12 add CHECK ( logdate >= DATE ‘2017-12-01’ AND logdate < DATE ‘2018-01-01’ );
4,對於每個分割槽,在關鍵字欄位上建立一個索引,以及其它你想建立的索引。關鍵字欄位索引並非嚴格必需的,但是在大多數情況下它是很有幫助的。

create index measurement_y2017m01_logdate on measurement_y2017m01 (logdate);
create index measurement_y2017m02_logdate on measurement_y2017m02 (logdate);
create index measurement_y2017m03_logdate on measurement_y2017m03 (logdate);
create index measurement_y2017m04_logdate on measurement_y2017m04 (logdate);
create index measurement_y2017m05_logdate on measurement_y2017m05 (logdate);
create index measurement_y2017m06_logdate on measurement_y2017m06 (logdate);
create index measurement_y2017m07_logdate on measurement_y2017m07 (logdate);
create index measurement_y2017m08_logdate on measurement_y2017m08 (logdate);
create index measurement_y2017m09_logdate on measurement_y2017m09 (logdate);
create index measurement_y2017m10_logdate on measurement_y2017m10 (logdate);
create index measurement_y2017m11_logdate on measurement_y2017m11 (logdate);
create index measurement_y2017m12_logdate on measurement_y2017m12 (logdate);
5,另外,定義一個規則或者觸發器,來重定向資料插入主表到適當的分割槽。

CREATE OR REPLACE FUNCTION measurement_insert_trigger()
RETURNS TRIGGER AS $$
BEGIN
IF ( NEW.logdate >= DATE ‘2017-01-01’ AND
NEW.logdate < DATE ‘2017-02-01’ ) THEN
INSERT INTO measurement_y2017m01 VALUES (NEW.*);

ELSIF ( NEW.logdate >= DATE ‘2017-02-01’ AND
NEW.logdate < DATE ‘2017-03-01’ ) THEN
INSERT INTO measurement_y2017m02 VALUES (NEW.*);

ELSIF ( NEW.logdate >= DATE ‘2017-03-01’ AND
NEW.logdate < DATE ‘2017-04-01’ ) THEN
INSERT INTO measurement_y2017m03 VALUES (NEW.*);

ELSIF ( NEW.logdate >= DATE ‘2017-04-01’ AND
NEW.logdate < DATE ‘2017-05-01’ ) THEN
INSERT INTO measurement_y2017m04 VALUES (NEW.*);

ELSIF ( NEW.logdate >= DATE ‘2017-05-01’ AND
NEW.logdate < DATE ‘2017-06-01’ ) THEN
INSERT INTO measurement_y2017m05 VALUES (NEW.*);

ELSIF ( NEW.logdate >= DATE ‘2017-06-01’ AND
NEW.logdate < DATE ‘2017-07-01’ ) THEN
INSERT INTO measurement_y2017m06 VALUES (NEW.*);

ELSIF ( NEW.logdate >= DATE ‘2017-07-01’ AND
NEW.logdate < DATE ‘2017-08-01’ ) THEN
INSERT INTO measurement_y2017m07 VALUES (NEW.*);

ELSIF ( NEW.logdate >= DATE ‘2017-08-01’ AND
NEW.logdate < DATE ‘2017-09-01’ ) THEN
INSERT INTO measurement_y2017m08 VALUES (NEW.*);

ELSIF ( NEW.logdate >= DATE ‘2017-09-01’ AND
NEW.logdate < DATE ‘2017-10-01’ ) THEN
INSERT INTO measurement_y2017m09 VALUES (NEW.*);

ELSIF ( NEW.logdate >= DATE ‘2017-10-01’ AND
NEW.logdate < DATE ‘2017-11-01’ ) THEN
INSERT INTO measurement_y2017m10 VALUES (NEW.*);

ELSIF ( NEW.logdate >= DATE ‘2017-11-01’ AND
NEW.logdate < DATE ‘2017-12-01’ ) THEN
INSERT INTO measurement_y2017m11 VALUES (NEW.*);

ELSIF ( NEW.logdate >= DATE ‘2017-12-01’ AND
NEW.logdate < DATE ‘2018-01-01’ ) THEN
INSERT INTO measurement_y2017m12 VALUES (NEW.*);

ELSE
RAISE EXCEPTION ‘Date out of range. Fix the measurement_insert_trigger() function!’;
END IF;
RETURN NULL;
END;
$$
LANGUAGE plpgsql;

create trigger insert_measurement_trigger
before insert on measurement
for each row execute procedure measurement_insert_trigger();
注意,每一個IF測試必須匹配其分割槽的 CHECK約束。

6,確保postgresql.conf裡的配置引數constraint_exclusion是開啟的。沒有這個引數,查詢不會按照需要進行優化。

約束排除

約束排除是一種查詢優化技巧,它改進了用上述方法定義的表分割槽的效能。比如:

SET constraint_exclusion = on;
SELECT count(*) FROM measurement WHERE logdate >= DATE ‘2017-11-22’;
如果沒有約束排除,上面的查詢會掃描measurement表中的每一個分割槽。打開了約束排除之後,規劃器將檢查每個分割槽的約束然後試圖證明該分割槽不需要被掃描 (因為它不能包含任何符合WHERE子句條件的資料行)。如果規劃器可以證明這個,它就把該分割槽從查詢規劃裡排除出去。

postgres=# set constraint_exclusion = off;
SET
postgres=# explain select count(*) from measurement where logdate >= ‘2017-11-22’;
QUERY PLAN

Aggregate (cost=338.27…338.28 rows=1 width=0)
-> Append (cost=0.00…319.76 rows=7405 width=0)
-> Seq Scan on measurement (cost=0.00…0.00 rows=1 width=0)
Filter: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m01 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m01_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m02 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m02_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m03 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m03_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m04 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m04_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m05 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m05_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m06 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m06_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m07 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m07_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m08 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m08_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m09 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m09_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m10 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m10_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m11 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m11_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m12 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m12_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
(52 rows)
使用EXPLAIN命令顯示一個規劃在constraint_exclusion 開啟和關閉情況下的不同。

postgres=# set constraint_exclusion = on;
SET
postgres=# explain select count(*) from measurement where logdate >= ‘2017-11-22’;
QUERY PLAN

Aggregate (cost=56.38…56.39 rows=1 width=0)
-> Append (cost=0.00…53.29 rows=1235 width=0)
-> Seq Scan on measurement (cost=0.00…0.00 rows=1 width=0)
Filter: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m11 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m11_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Heap Scan on measurement_y2017m12 (cost=8.93…26.65 rows=617 width=0)
Recheck Cond: (logdate >= ‘2017-11-22’::date)
-> Bitmap Index Scan on measurement_y2017m12_logdate (cost=0.00…8.78 rows=617 width=0)
Index Cond: (logdate >= ‘2017-11-22’::date)
(12 rows)
請注意,約束排除只由CHECK約束驅動,而不會由索引驅動。因此,在關鍵字欄位上定義索引是沒有必要的。

傳統分割槽表原理

傳統上使用表繼承來實現PostgreSQL中的分割槽。每個分割槽必須建立為具有CHECK CONSTRAINT的子表,定義觸發器或RULE來重定向資料插入主表到適當的分割槽,由於查詢和更新涉及約束的檢查、插入則涉及觸發器或規則重寫,導致分割槽功能效能較差。

pg_pathman

pg_pathman 是一款PostgreSQL分割槽工具,pg_pathman模組提供了優化的分割槽機制和功能來管理分割槽。pg_pathman現在穩定的版本是1.2.1,只支援PostgreSQL 9.5以及以上的版本。

特性

HASH和RANGE分割槽方案
自動和手動分割槽管理
支援整數,浮點數,日期等型別,包括域名
分割槽表(JOIN,子選擇等)的有效查詢計劃
RuntimeAppend&RuntimeMergeAppend自定義計劃節點在執行時選擇分割槽
PartitionFilter:INSERT觸發器的高效插入式替換
自動分割槽建立新的INSERTED資料(僅適用於RANGE分割槽)
改進了能夠將行直接插入分割槽的COPY FROM \ TO語句
UPDATE觸發生成開箱即用(也將替換為自定義節點)
分割槽建立事件處理的使用者定義回撥
非阻塞並發表分割槽
FDW支援(外部表)
各種GUC切換和可配置設定
安裝

unzip pg_pathman-1.2.1.zip

cd pg_pathman-1.2.1

make USE_PGXS=1

make USE_PGXS=1 install

cd $PGDATA

vi postgresql.conf

shared_preload_libraries = ‘pg_pathman’

pg_ctl restart -m fast

psql

postgres=# create extension pg_pathman;
CREATE EXTENSION
postgres=# \dx
List of installed extensions
Name | Version | Schema | Description
------------±--------±-----------±-----------------------------
pg_pathman | 1.2 | public | Partitioning tool
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
(2 rows)
檢視和表

pg_pathman 使用函式來維護分割槽表,並且建立了一些檢視,可以檢視分割槽表的狀態。

分割槽表的定義則存在一張表中,定義資料快取在記憶體中。,其中range使用binary search查詢對應的分割槽,hash使用hash search查詢對應的分割槽,相比查詢時通過約束過濾更加高效。

postgres=# \d
List of relations
Schema | Name | Type | Owner
--------±------------------------------±------±---------
public | pathman_concurrent_part_tasks | view | postgres
public | pathman_config | table | postgres
public | pathman_config_params | table | postgres
public | pathman_partition_list | view | postgres
(4 rows)
pathman_config — main config storage

CREATE TABLE IF NOT EXISTS pathman_config (
partrel REGCLASS NOT NULL PRIMARY KEY, --主表OID
attname TEXT NOT NULL, --分割槽列名
parttype INTEGER NOT NULL, --分割槽型別(RANGE OR HASH)
range_interval TEXT); --RANGE分割槽的間隔
pathman_config_params — optional parameters

CREATE TABLE IF NOT EXISTS pathman_config_params (
partrel REGCLASS NOT NULL PRIMARY KEY, --主表OID
enable_parent BOOLEAN NOT NULL DEFAULT TRUE, --是否在優化器中過濾主表
auto BOOLEAN NOT NULL DEFAULT TRUE, --insert時是否自動擴充套件分割槽
init_callback REGPROCEDURE NOT NULL DEFAULT 0,–create partition時的回撥函式
spawn_using_bgw BOOLEAN NOT NULL DEFAULT FALSE);
pathman_concurrent_part_tasks — currently running partitioning workers
– helper SRF function
CREATE OR REPLACE FUNCTION show_concurrent_part_tasks()
RETURNS TABLE (
userid REGROLE,
pid INT,
dbid OID,
relid REGCLASS,
processed INT,
status TEXT)
AS ‘pg_pathman’, ‘show_concurrent_part_tasks_internal’
LANGUAGE C STRICT;

CREATE OR REPLACE VIEW pathman_concurrent_part_tasks
AS SELECT * FROM show_concurrent_part_tasks();
pathman_partition_list — list of all existing partitions
– helper SRF function
CREATE OR REPLACE FUNCTION show_partition_list()
RETURNS TABLE (
parent REGCLASS,
partition REGCLASS,
parttype INT4,
partattr TEXT,
range_min TEXT,
range_max TEXT)
AS ‘pg_pathman’, ‘show_partition_list_internal’
LANGUAGE C STRICT;

CREATE OR REPLACE VIEW pathman_partition_list
AS SELECT * FROM show_partition_list();
pg_pathman 原理

pg_pathman模組提供了很多函式功能來管理分割槽,方便快捷建立管理分割槽。pg_pathman與傳統的繼承分割槽表做法有一個不同的地方,分割槽的定義存放在一張元資料表中,表的資訊會cache在記憶體中,同時使用HOOK來實現RELATION的替換,所以效率非常高。

pg_pathman 用到的hook如下

  1. pg_pathman uses ProcessUtility_hook hook to handle COPY queries for partitioned tables.

  2. RuntimeAppend (overrides Append plan node)

  3. RuntimeMergeAppend (overrides MergeAppend plan node)

  4. PartitionFilter (drop-in replacement for INSERT triggers)

提供的函式

建立分割槽表

HASH 分割槽

語法:
create_hash_partitions(relation REGCLASS,
attribute TEXT,
partitions_count INTEGER,
partition_data BOOLEAN DEFAULT TRUE,
partition_names TEXT[] DEFAULT NULL,
tablespaces TEXT[] DEFAULT NULL)

引數: relation REGCLASS – 主表OID
attribute TEXT – 分割槽列名
partitions_count INTEGER – 建立多少個分割槽
partition_data BOOLEAN DEFAULT TRUE – 是否將資料從主表遷移到分割槽
RANGE 分割槽

RANGE分割槽建立方式有兩種
(1)指定開始值,間隔值,分割槽個數

語法:
create_range_partitions(relation REGCLASS,
attribute TEXT,
start_value ANYELEMENT,
p_interval ANYELEMENT,
p_count INTEGER DEFAULT NULL
partition_data BOOLEAN DEFAULT TRUE)

引數: relation REGCLAS – 主表OID
attribute TEXT – 分割槽列名
start_value ANYELEMENT – 開始值
p_interval ANYELEMENT – 間隔;任意型別,適合任意型別的分割槽表
p_count INTEGER DEFAULT NULL – 分多少個區

語法:
create_range_partitions(relation REGCLASS,
attribute TEXT,
start_value ANYELEMENT,
p_interval INTERVAL,
p_count INTEGER DEFAULT NULL,
partition_data BOOLEAN DEFAULT TRUE)

引數: relation REGCLASS – 主表OID
attribute TEXT – 分割槽列名
start_value ANYELEMENT – 開始值
p_interval INTERVAL – 間隔;intel型別,用於時間分割槽表
p_count INTEGER DEFAULT NULL – 分多少個區
partition_data BOOLEAN DEFAULT TRUE – 是否將資料從主表遷移到分割槽
(2)指定開始值,結束值,間隔值

語法:
create_partitions_from_range(relation REGCLASS,
attribute TEXT,
start_value ANYELEMENT,
end_value ANYELEMENT,
p_interval ANYELEMENT,
partition_data BOOLEAN DEFAULT TRUE)

引數: relation REGCLASS – 主表OID
attribute TEXT – 分割槽列名
start_value ANYELEMENT – 開始值
end_value ANYELEMENT – 結束值
p_interval ANYELEMENT – 間隔;任意型別,適合任意型別的分割槽表
partition_data BOOLEAN DEFAULT TRUE – 是否將資料從主表遷移到分割槽

語法:
create_partitions_from_range(relation REGCLASS,
attribute TEXT,
start_value ANYELEMENT,
end_value ANYELEMENT,
p_interval INTERVAL,
partition_data BOOLEAN DEFAULT TRUE)

引數: relation REGCLASS – 主表OID
attribute TEXT – 分割槽列名
start_value ANYELEMENT – 開始值
end_value ANYELEMENT – 結束值
p_interval INTERVAL – 間隔;interval 型別,用於時間分割槽表
partition_data BOOLEAN DEFAULT TRUE – 是否將資料從主表遷移到分割槽
資料遷移

功能:

如果建立分割槽表時,未將主表資料遷移到分割槽,那麼可以使用非堵塞式的遷移介面,將資料遷移到分割槽。

語法:
partition_table_concurrently(relation REGCLASS,
batch_size INTEGER DEFAULT 1000,
sleep_time FLOAT8 DEFAULT 1.0)

引數: relation REGCLASS, – 主表OID
batch_size INTEGER DEFAULT 1000, – 一個事務批量遷移多少資料
sleep_time FLOAT8 DEFAULT 1.0) – 獲得行鎖失敗時,休眠多久再次獲取,重試60次退出任務
分裂合併分割槽

說明:僅支援範圍分割槽,合併分割槽時指定兩個需要合併分割槽,必須為相鄰分割槽

語法:
split_range_partition(partition REGCLASS,
split_value ANYELEMENT,
partition_name TEXT DEFAULT NULL)

引數: partition REGCLASS – 分割槽oid
split_value ANYELEMENT – 分裂值
partition_name TEXT DEFAULT NULL – 分裂後新增的分割槽表名

語法:
merge_range_partitions(partition1 REGCLASS, partition2 REGCLASS)
繫結分割槽

功能:將已有的表,繫結到已有的某個分割槽主表。已有的表與主表要保持一致的結構

語法:
attach_range_partition(relation REGCLASS,
partition REGCLASS,
start_value ANYELEMENT,
end_value ANYELEMENT)

引數: relation REGCLASS – 主表OID
partition REGCLASS – 分割槽表OID
start_value ANYELEMENT – 起始值
end_value ANYELEMENT – 結束值
解綁分割槽

功能:將分割槽從主表的繼承關係中刪除, 不刪資料,刪除繼承關係,刪除約束

語法:
detach_range_partition(partition REGCLASS)

引數: partition REGCLASS – 指定分割槽名,轉換為普通表
刪除分割槽

語法:
drop_range_partition(partition TEXT,
delete_data BOOLEAN DEFAULT TRUE)

引數: partition TEXT, – 分割槽名稱
delete_data BOOLEAN DEFAULT TRUE) – 是否刪除分割槽資料,如果false,表示分割槽資料遷移到主表
其他引數

功能:禁用主表
語法:
set_enable_parent(relation REGCLASS, value BOOLEAN)

功能:啟用/禁用自動擴充套件分割槽(僅適用於RANGE分割槽)。預設情況下啟用
語法:
set_auto(relation REGCLASS, value BOOLEAN)

說明:不建議開啟自動擴充套件分割槽,如果插入範圍闊度很大會插入很多分割槽
示例(範圍分割槽):

建立一張表:

CREATE TABLE journal (
id SERIAL,
dt TIMESTAMP NOT NULL,
level INTEGER,
msg TEXT);

– similar index will also be created for each partition
CREATE INDEX ON journal(dt);

– generate some data
INSERT INTO journal (dt, level, msg)
SELECT g, random() * 6, md5(g::text)
FROM generate_series(‘2015-01-01’::date, ‘2015-12-31’::date, ‘1 minute’) as g;
使用create_range_partitions()函式建立分割槽,使每個分割槽將包含一天的資料:

SELECT create_range_partitions(‘journal’, ‘dt’, ‘2015-01-01’::date, ‘1 day’::interval);
它將建立365個分割槽,並將資料從父級移動到分割槽。

新增新分割槽:

– add new partition with specified range
SELECT add_range_partition(‘journal’, ‘2016-01-01’::date, ‘2016-01-07’::date);

– append new partition with default range
SELECT append_range_partition(‘journal’);
第一個建立一個指定範圍的分割槽。第二個建立具有預設間隔的分割槽,並將其附加到分割槽列表。也可以將現有表作為分割槽附加。

CREATE FOREIGN TABLE journal_archive (
id INTEGER NOT NULL,
dt TIMESTAMP NOT NULL,
level INTEGER,
msg TEXT)
SERVER archive_server;

SELECT attach_range_partition(‘journal’, ‘journal_archive’, ‘2014-01-01’::date, ‘2015-01-01’::date);
重要提示:附加表的定義必須與現有分割槽表之一相匹配,包括已刪除的列。
要合併到相鄰的分割槽,使用merge_range_partitions()函式:

SELECT merge_range_partitions(‘journal_archive’, ‘journal_1’);
要按值拆分分割槽,使用split_range_partition()函式:

SELECT split_range_partition(‘journal_366’,‘2016-01-03’:: date);
要分離分割槽,使用detach_range_partition()函式:

SELECT detach_range_partition(‘journal_archive’);
pg_pathman 與 傳統分割槽表 效能對比

20分割槽/2000w資料量批量插入效能對比。

表結構如下:

postgres=# \d test_pg_part_pathman
Table “public.test_pg_part_pathman”
Column | Type | Modifiers
----------±----------------------------±----------
id | integer | not null
info | text |
crt_time | timestamp without time zone |
Indexes:
“test_pg_part_pathman_pkey” PRIMARY KEY, btree (id)
Number of child tables: 20 (Use \d+ to list them.)
測試結果:

單行記錄效能對比:

結果立竿見影,pg_pathman的效能遠遠高於傳統分割槽表。

相關連結:

PostgreSQL 10

PostgreSQL 9.6 Beta1 已經集成了內建分割槽表功能,支援range和list分割槽,內建分割槽直接使用插入更新刪除介面處理,節省了parser和optimize的過程,比觸發器轉成SQL更高效。

功能(9.6 Beta1)

建立主表

CREATE TABLE table_name ( … ) PARTITION BY RANGE(column1, column2, … );
CREATE TABLE table_name ( … ) PARTITION BY LIST(column1, column2, … );
建立分割槽

CREATE TABLE partition_name PARTITION OF table_name FOR
VALUES START (value1, value2, …)/UNBOUNDED EXCLUSIVE/INCLUSIVE
END (values1, values2, …)/UNBOUNDED EXCLUSIVE/INCLUSIVE;

CREATE TABLE partition_name PARTITION OF table_name FOR
VALUES IN (value1, value2, …);
說明:

UNBOUNDED關鍵字表示無限大或無限小。
EXCLUSIVE 表示約束範圍不包括上下限值,INCLUSIVE包括。預設START是INCLUSIVE,END是EXCLUSIVE。
內建分割槽也是在繼承基礎上實現的。
繫結分割槽

ALTER TABLE table_name ATTACH PARTITION table_name1 FOR
VALUES START (value1, value2, …)/UNBOUNDED EXCLUSIVE/INCLUSIVE
END (values1, values2, …)/UNBOUNDED EXCLUSIVE/INCLUSIVE VALIDATE/NO VALIDATE;

ALTER TABLE table_name ATTACH PARTITION table_name1 FOR
VALUES IN (value1, value2, …) VALIDATE/NO VALIDATE;
解綁分割槽

ALTER TABLE table_name DETACH PARTITION table_name1;
刪除分割槽

ALTER TABLE table_name DETACH PARTITION table_name1;
DROP TABLE table_name1;
說明:

內建分割槽不能直接刪除分割槽,需要先DETACH,再以普通表刪除。

分割槽支援對比

PostgreSQL 10支援range,list分割槽表,同時hash分割槽處於POC階段(同時還有一些需要改進的地方,例如優化器部分)。

以下是分割槽型別的不同支援:

分割槽型別的不同支援

RANGE	LIST	HASH

傳統分割槽表 支援 支援
pg_pathman 支援 支援
9.6 Beta1 支援 支援
Postgres 10.0 支援 支援 支援
PostgreSQL 10 支援更多的型別的分割槽,還將支援外部表作為分割槽,10的分割槽功能很值得期待…

COPY 500W 記錄的效能對比(pg 9.6 Beta1)

表結構:

CREATE TABLE test
(
id int,
info text,
crt_time timestamp
) PARTITION BY RANGE (id);
測試結果:

Pg_pathman 與內建分割槽(9.6 Beta1)效能對比

下面測試他們的插入速度,10分割槽/100W資料量。

表結構如下:

create table tb_test
(
id int not null,
firstname text,
lastname text,
corp text,
createtime timestamp without time zone
) partition by range(id);
測試結果如下:

可以發現pg_pathman與9.6 Bate1內建分割槽差距不大。畢竟還只是測試版,在pg 10 中效能會更加優越。

相關連結:

總結

這裡簡單介紹了PostgreSQL分割槽的概念,傳統分割槽表的實現以及約束排除,分割槽功能擴充套件pg_pathman的原理,高效的效能以及提供的分割槽管理功能,並與傳統分割槽表做了效能對比,pg內建分割槽與傳統分割槽,pg_pathman的效能對比。