Partition-wise join

什麼是「partition-wise join」呢?咱們將用一個比喻來解釋它的好處。sql

假設兩我的,Logan和Shannon,決定住在一塊兒。若是他們每一個人都已經有了本身的住所,他們就會擁有不少你在任何家庭都能找到的普通物品。因此他們要作一個決定——是每樣東西都保留兩件,仍是對它們的共同點進行「篩選」。在這個假想的場景中,咱們將關注浴室和廚房中的家庭用品。Logan拿起一套廚刀和一個刀板,打電話問Shannon:「嘿,Shannon,你已經有一個刀架了嗎?」數據庫

你以爲Shannon會怎麼作?在整棟房子裏找一個現有的刀板?固然不是。若是有刀板,那麼它惟一的位置就是廚房。事實上,當Shannon和Logan在整所房子裏匹配物品時,他們會把調查範圍限制在對有問題的物品有意義的房間裏。這是常識——爲何會有人在浴室裏找(好比說)叉子和勺子呢?那只是白費力氣。(編者注:任何有小孩的人固然會對這個比喻提出異議,他們會很是正確地指出,你可能在每一個房間裏都能找到全部可能的家居用品,可能在外面也能找到,但爲了討論的方便,咱們將省略這種可能性)less


這正是Partition-Wise Join使咱們可以在數據庫中作到的。若是兩個表分區具備相同的定義,咱們在分區鍵上進行join,那麼定義保證對於表中位於分區P、分區鍵爲k的行,咱們只須要在要鏈接的表中的相同的分區中找對應的行(相同是基於分區定義的)。這種劃分至關於「只在廚房裏搜索,而不是在浴室」。在執行這樣的鏈接時,咱們能夠經過執行計劃看到這一點。讓咱們建立兩個分區定義相等的表,而後在分區鍵上鍊接。編輯器

SQL> --
SQL> -- Example 1
SQL> --
SQL>
SQL> drop table t1 purge;

Table dropped.

SQL> drop table t2 purge;

Table dropped.

SQL>
SQL> create table t1 ( x int, y int )
  2  partition by range ( x )
  3  (
  4  partition p_kitchen values less than (10000),
  5  partition p_bathroom values less than (20000),
  6  partition p_dining values less than (30000)
  7  );

Table created.

SQL>
SQL>
SQL> create table t2 ( x int, y int )
  2  partition by range ( x )
  3  (
  4  partition p_kitchen values less than (10000),
  5  partition p_bathroom values less than (20000),
  6  partition p_dining values less than (30000)
  7  );

Table created.

SQL>
SQL>
SQL> insert into t1 select rownum, rownum from dual connect by level < 30000;

29999 rows created.

SQL> insert into t2 select * from t1;

29999 rows created.

SQL> commit;

Commit complete.

SQL> exec dbms_stats.gather_table_stats('','t1')

PL/SQL procedure successfully completed.

SQL> exec dbms_stats.gather_table_stats('','t2')

PL/SQL procedure successfully completed.

SQL>
SQL> --
SQL> set autotrace traceonly explain
SQL> select count(t1.y), count(t2.y)
  2  from t1,t2
  3  where t1.x = t2.x;

Execution Plan
----------------------------------------------------------
Plan hash value: 3155849676

---------------------------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |     1 |    20 |  1641   (1)| 00:00:01 |       |       |
|   1 |  SORT AGGREGATE      |      |     1 |    20 |            |          |       |       |
|   2 |   PARTITION RANGE ALL|      | 29999 |   585K|  1641   (1)| 00:00:01 |     1 |     3 |
|*  3 |    HASH JOIN         |      | 29999 |   585K|  1641   (1)| 00:00:01 |       |       |
|   4 |     TABLE ACCESS FULL| T1   | 29999 |   292K|   820   (1)| 00:00:01 |     1 |     3 |
|   5 |     TABLE ACCESS FULL| T2   | 29999 |   292K|   820   (1)| 00:00:01 |     1 |     3 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("T1"."X"="T2"."X")

SQL> set autotrace off
SQL>

 這裏執行計劃的關鍵部分是,hash join發生在分區範圍內(或「under」分區範圍內)的全部迭代中。這能夠解釋爲:「從每一個表中的第一個分區開始,對該分區執行散列鏈接。而後移動到下一個分區;在那個分區上執行散列鏈接」,等等。這在資源上是有效的,由於在任何狀況下咱們都不會嘗試(顯然是失敗的)將表T1分區P_KITCHEN鏈接到表T2分區P_BATHROOM或P_DINING。每一個散列鏈接是一個較小的操做,所以也更有可能在該會話的可用PGA分配中完成。並且,當涉及到並行運行這樣的查詢時,每一個並行的奴隸進程均可以處理將一個分區對其餘奴隸進程隔離的工做。ide

 

若是分區沒有對齊(請參閱前面的編輯器註釋),那麼咱們的鏈接就沒有那麼有效。this

SQL> --
SQL> -- Example 2
SQL> --
SQL>
SQL>
SQL> drop table t2 purge;

Table dropped.

SQL> create table t2 ( x int, y int )
  2  partition by range ( x )
  3  (
  4  partition p1 values less than (15000),
  5  partition p3 values less than (30000)
  6  );

Table created.

SQL>
SQL> --
SQL> -- all partitions do NOT align, so we do NOT see partition-wise join
SQL> --
SQL>
SQL> insert into t2 select * from t1;

29999 rows created.

SQL> commit;

Commit complete.

SQL> exec dbms_stats.gather_table_stats('','t2')

PL/SQL procedure successfully completed.

SQL> set autotrace traceonly explain
SQL> select count(t1.y), count(t2.y)
  2  from t1,t2
  3  where t1.x = t2.x;

Execution Plan
----------------------------------------------------------
Plan hash value: 666786458

---------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |     1 |    20 |  1369   (1)| 00:00:01 |       |       |
|   1 |  SORT AGGREGATE               |         |     1 |    20 |            |          |       |       |
|*  2 |   HASH JOIN                   |         | 29999 |   585K|  1369   (1)| 00:00:01 |       |       |
|   3 |    PART JOIN FILTER CREATE    | :BF0000 | 29999 |   585K|  1369   (1)| 00:00:01 |       |       |
|   4 |     PARTITION RANGE ALL       |         | 29999 |   292K|   820   (1)| 00:00:01 |     1 |     3 |
|   5 |      TABLE ACCESS FULL        | T1      | 29999 |   292K|   820   (1)| 00:00:01 |     1 |     3 |
|   6 |    PARTITION RANGE JOIN-FILTER|         | 29999 |   292K|   548   (1)| 00:00:01 |:BF0000|:BF0000|
|   7 |     TABLE ACCESS FULL         | T2      | 29999 |   292K|   548   (1)| 00:00:01 |:BF0000|:BF0000|
---------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("T1"."X"="T2"."X")

Note
-----
   - this is an adaptive plan

SQL> set autotrace off
SQL>
SQL>

 這裏的關鍵元素是散列鏈接如今位於全部分區的循環之上。在Oracle的早期版本中,你不會看到包含:BF0000的行,所以這是一個跨全部行的簡單聯接,就好像這些表根本沒有分區同樣。可是當分區不一致時,在現代版本中狀況會稍微好一些。咱們使用「Bloom filter」(所以:BF前綴)來減小鏈接兩個表的開銷。既然我在這篇文章中使用了比喻,那就想一想「提早打電話」到電影院,看看有沒有你最喜歡的電影的座位。若是電影院老闆說電影已經賣完了,你就省了一趟車。但只是由於影院老闆說還有空座,你仍是有可能開車去那裏,發現電影在那段時間已經賣完了。「Bloom filter」就像提早打電話同樣——你頗有可能能夠避免一些工做,但這並非一個保證。你能夠在Christian Antognini的一篇偉大的白皮書中讀到有關「Bloom filter」的內容。orm

注意,全部的分區必須對齊。下面是一個示例,其中前三個分區是對齊的,邊界是10000、20000和30000,可是第二個表T2定義了一個額外的分區。再一次,咱們回到了Bloom filter選項。blog

SQL> --
SQL> -- Example 3
SQL> --
SQL> drop table t2 purge;

Table dropped.

SQL> create table t2 ( x int, y int )
  2  partition by range ( x )
  3  (
  4  partition p1 values less than (10000),
  5  partition p2 values less than (20000),
  6  partition p3 values less than (30000),
  7  partition p4 values less than (40000)
  8  );

Table created.

SQL>
SQL> --
SQL> -- all partitions do NOT align, so we do NOT see partition-wise join
SQL> --
SQL>
SQL> insert into t2 select rownum, rownum from dual connect by level < 40000;

39999 rows created.

SQL> commit;

Commit complete.

SQL> exec dbms_stats.gather_table_stats('','t2')

PL/SQL procedure successfully completed.

SQL> set autotrace traceonly explain
SQL> select count(t1.y), count(t2.y)
  2  from t1,t2
  3  where t1.x = t2.x;

Execution Plan
----------------------------------------------------------
Plan hash value: 666786458

---------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |     1 |    20 |  1913   (1)| 00:00:01 |       |       |
|   1 |  SORT AGGREGATE               |         |     1 |    20 |            |          |       |       |
|*  2 |   HASH JOIN                   |         | 29999 |   585K|  1913   (1)| 00:00:01 |       |       |
|   3 |    PART JOIN FILTER CREATE    | :BF0000 | 29999 |   585K|  1913   (1)| 00:00:01 |       |       |
|   4 |     PARTITION RANGE ALL       |         | 29999 |   292K|   820   (1)| 00:00:01 |     1 |     3 |
|   5 |      TABLE ACCESS FULL        | T1      | 29999 |   292K|   820   (1)| 00:00:01 |     1 |     3 |
|   6 |    PARTITION RANGE JOIN-FILTER|         | 39999 |   390K|  1093   (1)| 00:00:01 |:BF0000|:BF0000|
|   7 |     TABLE ACCESS FULL         | T2      | 39999 |   390K|  1093   (1)| 00:00:01 |:BF0000|:BF0000|
---------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("T1"."X"="T2"."X")

Note
-----
   - this is an adaptive plan

SQL> set autotrace off
SQL>
SQL>
SQL>

 所以,對分區表進行更快的查詢不只僅是關於分區修剪。Partition-wise joins還能夠對查詢響應時間產生有益的影響。進程

 

https://connor-mcdonald.com/2017/09/21/partition-wise-join/資源

相關文章
相關標籤/搜索