與基於繼承的分區(inheritance-based partitioning)不一樣,PostgreSQL 10中引入的聲明式分區對數據如何劃分沒有任何影響。PostgreSQL 11的查詢優化器正準備利用這種「無推理」表示。第一個提交的是partition-wise join。
什麼是partition-wise join
若是鏈接表的分區鍵之間存在相等鏈接條件,那麼兩個相似分區表之間的鏈接能夠分解爲它們的匹配分區之間的鏈接。分區鍵之間的等鏈接意味着一個分區表的給定分區中給定行的全部鏈接夥伴必須在另外一個分區表的相應分區中。所以,分區表之間的鏈接能夠分解爲匹配分區之間的鏈接。這種將分區表之間的鏈接分解爲分區之間的鏈接的技術稱爲partition-wise join。sql
PostgreSQL中的partition-wise join服務器
讓咱們從一個例子開始。考慮按以下方式分區的兩個表:app
create table prt1 (a int, b int, c varchar) partition by range(a); create table prt1_p1 partition of prt1 for values from (0) to (5000); create table prt1_p2 partition of prt1 for values from (5000) to (15000); create table prt1_p3 partition of prt1 for values from (15000) to (30000); create table prt2 (a int, b int, c varchar) partition by range(b); create table prt2_p1 partition of prt2 for values from (0) to (5000); create table prt2_p2 partition of prt2 for values from (5000) to (15000); create table prt2_p3 partition of prt2 for values from (15000) to (30000);
prt1_p1中一行的全部鏈接夥伴都來自prt2_p1。
prt1_p2中一行的全部鏈接夥伴都來自prt2_p2。
而prt1_p3中一行的全部鏈接夥伴都來自prt2_p3。
這三個組成了匹配的分區對。沒有partition-wise join,這兩個表之間的鏈接計劃以下:oop
explain (costs off) select * from prt1 t1, prt2 t2 where t1.a = t2.b and t1.b = 0 and t2.b between 0 and 10000; QUERY PLAN ------------------------------------------------------- Hash Join Hash Cond: (t2.b = t1.a) -> Append -> Seq Scan on prt2_p1 t2 Filter: ((b >= 0) AND (b <= 10000)) -> Index Scan using prt2_p2_b on prt2_p2 t2_1 Index Cond: ((b >= 0) AND (b <= 10000)) -> Hash -> Append -> Seq Scan on prt1_p1 t1 Filter: (b = 0) -> Seq Scan on prt1_p2 t1_1 Filter: (b = 0) -> Seq Scan on prt1_p3 t1_2 Filter: (b = 0) (15 rows)
partition-wise join的加入計劃爲相同的查詢以下:優化
explain (costs off) select * from prt1 t1, prt2 t2 where t1.a = t2.b and t1.b = 0 and t2.b between 0 and 10000; QUERY PLAN ------------------------------------------------------------------------ Append -> Hash Join Hash Cond: (t2.b = t1.a) -> Seq Scan on prt2_p1 t2 Filter: ((b >= 0) AND (b <= 10000)) -> Hash -> Seq Scan on prt1_p1 t1 Filter: (b = 0) -> Nested Loop -> Seq Scan on prt1_p2 t1_1 Filter: (b = 0) -> Index Scan using prt2_p2_b on prt2_p2 t2_1 Index Cond: ((b = t1_1.a) AND (b >= 0) AND (b <= 10000)) (13 rows)
這裏有幾點須要注意:blog
1.存在一個等價鏈接條件t1.a=t2.b,包括來自兩個表的分區鍵。
2.在沒有partition-wise join的狀況下,鏈接將在「appending」來自任何分區表的每一個分區的全部行以後執行鏈接。對於partition-wise join,在匹配分區之間的鏈接後並附加結果。當鏈接結果的大小明顯小於叉乘的結果時,這是有利的。更有利的是,若是分區自己是外部表,即分區中的數據駐留在外部服務器上。
3.在沒有partition-wise join的狀況下,它使用散列鏈接,可是在partition-wise join的狀況下,它對分區之間的每一個鏈接使用不一樣的策略,爲每一個鏈接選擇最佳策略。例如,prt1_p2和prt2_p2之間的鏈接使用帶有prt2_p2_b索引掃描的嵌套循環鏈接做爲參數化的內端,而另外一個鏈接使用散列鏈接。
4.條件t2.b between 0和10000之間消除了分區prt2_p3,所以在沒有partition-wise join的狀況下不會被計劃掃描。可是它沒有注意到prt1_p3中的任何一行都沒有鏈接夥伴,而且仍然掃描該分區。使用partition-wise join,它意識到沒有匹配的分區,消除了對prt1_p3的掃描。消除整個分區是一個重大的改進,由於順序掃描很是昂貴。
排序
Partition-wise join優於未分區鏈接,由於它能夠利用分區的屬性,並使用更小的哈希表,這些哈希表可能徹底在內存中,更快的內存排序,在外部分區狀況下的鏈接下推,等等。繼承
基本的Partition-wise join以外索引
在提交的基本版本中,當鏈接表具備徹底相同的分區鍵數據類型並具備徹底匹配的分區邊界時,將應用該技術。但有幾個加強的可能性:內存
1.即便分區邊界不徹底匹配,當一個分區表中的每一個分區最多有一個與另外一個分區表匹配的分區時,也可使用該技術。目前正在爲此開發一個補丁。2.經過將未分區表與每一個分區分別鏈接併合並這些鏈接的結果,可使用此技術執行未分區表和已分區表之間的鏈接。當查詢中的一些表是未分區的,而其餘表是相似分區的,而且一個最佳計劃將分區表和未分區表交錯時,這可能會有所幫助。3.這種技術使用更多的內存和CPU,即便partition-wise join不是最佳策略。減小這種技術的內存和CPU佔用。4.當鏈接兩個不一樣分區的表時,對其中一個表從新分區以匹配另外一個表的分區方案,而後使用partition-wise join進行鏈接;一種一般有助於經過從新分佈數據來鏈接不一樣的切分表的技術。