一、什么是分區表
分區表就是將一個大表在物理上分割成若干小表,並且整個過程對用戶是透明的,也就是用戶的所有操作仍然是作用在大表上,不需要關心數據實際上落在哪張小表里面。Greenplum 中分區表的原理和 PostgreSQL 一樣,都是通過表繼承和約束實現的。
Greenplum 官方給出的分區表示例如下:
partitions.jpg
二、與分布的區別
分布:DISTRIBUTED
分區:PARTITION
Greenplum 中每個表都需要有一個分布鍵,如果你建表的時候沒有顯示使用語法DISTRIBUTED BY (column) 指定一個分布鍵,系統也會默認為你指定一個。分布目的是把數據打散到每個節點,打散的規則是 hash 或者 randomly。這樣在計算時可以充分利用每個節點的資源進行並行計算。
分區特性在本文會詳細介紹,兩者比較如下:
-
數據分布是在物理上拆分表數據,將數據打散到各個節點,使數據可以並行計算,這在 Greenplum 中是必須的。
-
表分區是在邏輯上拆分大表的數據提高查詢性能,也有利於數據生命周期的管理,這在 Greenplum 中是可選的。
-
無論是分區表還是非分區表,在 Greenplum 中,數據都是分散到各個節點上的。
-
分區不會影響數據在各個節點上的分布情況。
三、什么時候使用分區表
是否使用分區表,可以通過以下幾個方面進行考慮:
-
表數據量是否足夠大:通常對於大的事實表,比如數據量有幾千萬或者過億,我們可以考慮使用分區表,但數據量大小並沒有一個絕對的標准可以使用,一般是根據經驗,以及對目前性能是否滿意。
-
表是否有合適的分區字段:如果數據量足夠大了,這個時候我們就需要看下是否有合適的字段能夠用來分區,通常如果數據有時間維度,比如按天,按月等,是比較理想的分區字段。
-
表內數據是否具有生命周期:通常數倉中的數據不可能一直存放,一般都會有一定的生命周期,比如最近一年等,這里就涉及到對舊數據的管理,如果有分區表,就很容易刪除舊的數據,或者將舊的數據歸檔到對象存儲等更為廉價的存儲介質上。
-
查詢語句中是否含有分區字段:如果你對一個表做了分區,但是所有的查詢都不帶分區字段,這不僅無法提高性能反而會使性能下降,因為所有的查詢都會掃描所有的分區表。
四、創建分區表
Greenplum 支持三種分區類型:
-
范圍分區(Range Partition)
-
列表分區(List Partition)
-
組合分區(A combination of both types)
范圍分區例子:
CREATE TABLE test_range_partition ( uid int, fdate character varying(32) ) PARTITION BY RANGE(fdate) ( PARTITION p1 START ('2018-11-01') INCLUSIVE END ('2018-11-02') EXCLUSIVE, PARTITION p2 START ('2018-11-02') INCLUSIVE END ('2018-11-03') EXCLUSIVE, DEFAULT PARTITION pdefault ); 以上例子是按天建表,如果時間跨度比較大,會導致建表語句很長,書寫起來也不方便,這時候可以使用以下語法: CREATE TABLE test_range_partition_every_1 ( uid int, fdate date ) partition by range (fdate) ( PARTITION pn START ('2018-11-01'::date) END ('2018-12-01'::date) EVERY ('1 day'::interval), DEFAULT PARTITION pdefault );
列表分區例子:
CREATE TABLE test_list_partition ( uid int, gender char(1) ) PARTITION BY LIST (gender) ( PARTITION girls VALUES ('F'), PARTITION boys VALUES ('M'), DEFAULT PARTITION pdefault );
多級分區例子:
CREATE TABLE test_muti_level_partition ( uid int, gender char(1), fdate character varying(32) ) PARTITION BY RANGE(fdate) SUBPARTITION BY LIST(gender) SUBPARTITION template ( SUBPARTITION s1 VALUES ('F'), SUBPARTITION s2 VALUES ('M') ) ( PARTITION p1 START ('2018-11-01') INCLUSIVE END ('2018-11-02') EXCLUSIVE, PARTITION p2 START ('2018-11-02') INCLUSIVE END ('2018-11-03') EXCLUSIVE, DEFAULT PARTITION pdefault )
五、管理分區表
分區表也是一張表,所以對於表的很多操作也可以作用於分區表上,這里列舉了常用的一些操作:
清空子分區
ALTER TABLE test_range_partition TRUNCATE PARTITION p1;
刪除子分區
ALTER TABLE test_range_partition DROP PARTITION p1;
注:DROP PARTITION 之后跟的是 partition name,而不是 partition table name,這兩者之間是有區別的,如果是使用 EVERY 語法創建的分區表,你需要通過 pg_partitions 表查詢到對應分區的 partition name。
新增子分區
ALTER TABLE test_range_partition ADD PARTITION p3 START ('2018-11-03') INCLUSIVE END ('2018-11-04') EXCLUSIVE;
注:如果分區表中含有 DEFAULT 分區,會出現如下錯誤,解決辦法可以參見 滾動分區:
ERROR: cannot add RANGE partition "p3" to relation "test_range_partition" with DEFAULT partition "pdefault"
滾動分區
通常按時間分區的表,都有一個特性,就是分區會不斷往前滾動,比如一個按天分區,保存最近10天的分區表,每到新一天,就會要刪除10天前的分表表,並且創建一個新的分區表容納最新的數據。
如果是含有默認分區的,可以使用分區Split
ALTER TABLE test_range_partition SPLIT DEFAULT PARTITION START ('2018-11-03') INCLUSIVE END ('2018-11-04') EXCLUSIVE INTO (PARTITION p3, DEFAULT partition);
這樣新分區就被添加,同時保留了默認分區,然后在刪除老的分區就完成新老分區的更替。
交換分區
交換分區就是將一張普通的表和某張分區表進行交換,這個功能在數據分層存儲十分有用。
比如我們會需要根據對象存儲的不同目錄設置分區,這個需求就可以使用交換分區完成,這樣對於一張大表,他的較少查詢的歷史數據就可以放在對象存儲上,語法如下:
ALTER TABLE {table_name} EXCHANGE PARTITION {partition_name|FOR (RANK(number))|FOR (value)} WITH TABLE {cos_table_name} WITHOUT VALIDATION;
查詢分區
與分區相關的系統表或者視圖如下:
pg_partition
pg_partition_columns
pg_partition_encoding
pg_partition_rule
pg_partition_templates
pg_partitions
查看分區基本信息:
t2=# select * from pg_partitions where partitiontablename = 'test_range_partition_1_prt_p1'; -[ RECORD 1 ]------------+--------------------------------------------------------------------------------------------------- schemaname | public tablename | test_range_partition partitionschemaname | public partitiontablename | test_range_partition_1_prt_p1 partitionname | p1 parentpartitiontablename | parentpartitionname | partitiontype | range partitionlevel | 0 partitionrank | 1 partitionposition | 2 partitionlistvalues | partitionrangestart | '2018-11-01'::character varying(32) partitionstartinclusive | t partitionrangeend | '2018-11-02'::character varying(32) partitionendinclusive | f partitioneveryclause | partitionisdefault | f partitionboundary | PARTITION p1 START ('2018-11-01'::character varying(32)) END ('2018-11-02'::character varying(32)) parenttablespace | pg_default partitiontablespace | pg_default
查看分區定義
t2=# select pg_get_partition_def('test_range_partition'::regclass,true); -[ RECORD 1 ]--------+--------------------------------------------------------------------------------------------------------------- pg_get_partition_def | PARTITION BY RANGE(fdate) | ( | PARTITION p1 START ('2018-11-01'::character varying(32)) END ('2018-11-02'::character varying(32)), | PARTITION p2 START ('2018-11-03'::character varying(32)) END ('2018-11-04'::character varying(32)), | DEFAULT PARTITION pdefault | )
六、查詢優化
分區表很大的用途在於提升分析性能,但並不是對大表進行分區就能簡單的提升性能,也不是分區越多性能越好。
分區的粒度
通常像范圍分區的表都涉及到粒度問題,比如按時間分表,究竟是按天,按周,按月等。粒度越細,每張表的數據就越少,但是分區表的數量就會越多,反之亦然。
關於分區表的數量,這里沒有絕對的標准,一般來說分區表的數量在100左右已經算是比較多了。
分區表數目過多,會有多方面的影響,比如查詢優化器生成執行計划較慢,同時很多維護工作也都會變慢,比如 vacuum,recovering segment,expanding the cluster, checking disk usage 等。
查詢語句
為了充分利用分區表的優勢,需要在查詢語句中盡量帶上分區條件。最終目的是掃描盡量少的分區表。
如下是一個靜態分區消除的例子,可以看出 Partitions selected: 11 (out of 15),這里在15張分區表中選擇了其中11張
t2=# explain select * from test_range_partition_every_1 where fdate >= '2018-11-05'; QUERY PLAN -------------------------------------------------------------------------------------------------------- -------------------- Gather Motion 2:1 (slice1; segments: 2) (cost=0.00..431.00 rows=1 width=8) -> Sequence (cost=0.00..431.00 rows=1 width=8) -> Partition Selector for test_range_partition_every_1 (dynamic scan id: 1) (cost=10.00..100. 00 rows=50 width=4) Filter: fdate >= '2018-11-05'::date Partitions selected: 11 (out of 15) -> Dynamic Table Scan on test_range_partition_every_1 (dynamic scan id: 1) (cost=0.00..431.00 rows=1 width=8) Filter: fdate >= '2018-11-05'::date Optimizer status: PQO version 2.55.13 (8 rows)
注:Greenplum 最新一代的解析引擎 ORCA 是支持動態分區消除的,但是分區的選擇並不會打印在執行計划中。
以下是官網的說明:
For queries that involve dynamic partition selection where the partitioning key is compared to a variable, the number of partitions that are scanned will be known only during query execution. The partitions selected are not shown in the EXPLAIN output.
七、從Redshift 遷移到數據倉庫
使用過 Redshift 的朋友都知道,Redshift 是不支持分區表的,AWS 官方建議使用 sort key 和distribution key 來優化並行處理,官方建議如下:
CREATE TABLE Amazon Redshift does not support tablespaces, table partitioning, inheritance, and certain constraints. The Amazon Redshift implementation of CREATE TABLE enables you to define the sort and distribution algorithms for tables to optimize parallel processing. Amazon Redshift Spectrum supports table partitioning using the CREATE EXTERNAL TABLE command.
但是涉及到數據生命周期管理,Redshift 通常的做法是每個分區創建不同的表,而在所有表的基礎上創建一個視圖來管理這些表,仿造出一個分區的特性,這無疑是低效的。因此從Redshift 遷移過來的用戶建議在合適的場景下使用分區特性。
關注“騰訊雲大數據”公眾號,技術交流、最新活動、服務專享一站Get~