什么是“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/资源