什么是“partition-wise join”呢?我们将用一个比喻来解释它的好处。

假设两个人,Logan和Shannon,决定住在一起。如果他们每个人都已经有了自己的住所,他们就会拥有很多你在任何家庭都能找到的普通物品。所以他们要做一个决定——是每样东西都保留两件,还是对它们的共同点进行“筛选”。在这个假想的场景中,我们将关注浴室和厨房中的家庭用品。Logan拿起一套厨刀和一个刀板,打电话问Shannon:“嘿,Shannon,你已经有一个刀架了吗?”

你觉得Shannon会怎么做?在整栋房子里找一个现有的刀板?当然不是。如果有刀板,那么它唯一的位置就是厨房。事实上,当Shannon和Logan在整所房子里匹配物品时,他们会把调查范围限制在对有问题的物品有意义的房间里。这是常识——为什么会有人在浴室里找(比如说)叉子和勺子呢?那只是白费力气。(编者注:任何有小孩的人当然会对这个比喻提出异议,他们会非常正确地指出,你可能在每个房间里都能找到所有可能的家居用品,可能在外面也能找到,但为了讨论的方便,我们将省略这种可能性)

这正是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分配中完成。而且,当涉及到并行运行这样的查询时,每个并行的奴隶进程都可以处理将一个分区对其他奴隶进程隔离的工作。

如果分区没有对齐(请参阅前面的编辑器注释),那么我们的连接就没有那么有效。

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”的内容。

注意,所有的分区必须对齐。下面是一个示例,其中前三个分区是对齐的,边界是10000、20000和30000,但是第二个表T2定义了一个额外的分区。再一次,我们回到了Bloom filter选项。

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/

Partition-wise join的更多相关文章

  1. Oracle Partition Outer Join 稠化报表

    partition outer join实现将稀疏数据转为稠密数据,举例: with t as (select deptno, job, sum(sal) sum_sal from emp group ...

  2. oracle已知会导致错误结果的bug列表(Bug Issues Known to cause Wrong Results)

    LAST UPDATE:     1 Dec 15, 2016 APPLIES TO:     1 2 3 4 Oracle Database - Enterprise Edition - Versi ...

  3. 【原创】大数据基础之Spark(8)Spark中Join实现原理

    spark中join有两种,一种是RDD的join,一种是sql中的join,分别来看: 1 RDD join org.apache.spark.rdd.PairRDDFunctions /** * ...

  4. 009-Hadoop Hive sql语法详解4-DQL 操作:数据查询SQL-select、join、union、udtf

    一.基本的Select 操作 语法SELECT [ALL | DISTINCT] select_expr, select_expr, ...FROM table_reference[WHERE whe ...

  5. CMU Database Systems - Sorting,Aggregation,Join

    Sorting 排序如果可在内存里面排,用经典的排序算法就ok,比如快排 问题在于,数据表中的的数据是很多的,没法一下都放到内存里面进行排序 所以就需要用到,外排,多路并归排序 看下最简单的,2路并归 ...

  6. Hash Join: Basic Steps

    Joins https://docs.oracle.com/database/121/TGSQL/tgsql_join.htm#TGSQL242 tidb/index_lookup_hash_join ...

  7. 柯南君 :Oracle 分区技术 之 怎样支撑大数据操作?

    前段时间.看了罗女士( 资深技术顾问 - Oracle 中国 顾问咨询部)关于<大批量数据处理技术的演讲>视频.感觉受益良多,结合多年的知识积累,柯南君给大家分享一下: 交流内容: 一.O ...

  8. Oracle12c版本中未归档隐藏参数

    In this post, I will give a list of all undocumented parameters in Oracle 12.1.0.1c. Here is a query ...

  9. Oracle11g版本中未归档隐藏参数

    In this post, I will give a list of all undocumented parameters in Oracle 11g. Here is a query to se ...

  10. Spark RDD概念学习系列之RDD的依赖关系(宽依赖和窄依赖)(三)

    RDD的依赖关系?   RDD和它依赖的parent RDD(s)的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency). 1)窄依赖指的是每 ...

随机推荐

  1. linux服务器中安装VSCode

    Centos7系统 步骤:在linux系统中安装VSCode(Visual Studio Code) 1.从官网下载压缩包访问Visual Studio Code官网 https://code.vis ...

  2. Mac Docker安装MySQL5.7

    mkdir mysql 在~目录下创建mysql目录 docker run --restart=always --name mysql5.7 -p 3306:3306 -v ~/mysql:/var/ ...

  3. Python +urllib+urllib2 带数据的post请求实例

    #coding:utf-8 ''' Created on 2017年11月2日 @author: li.liu ''' import urllib import urllib2 import re f ...

  4. 最新NetMonitor代码

    <Window x:Class="NetMonitor.MainWindow" xmlns="http://schemas.microsoft.com/winfx/ ...

  5. Python3 - 基础(运算符)

    Python3-运算符 举个简单的例子 4 +5 = 9 . 例子中,4 和 5 被称为操作数,"+" 称为运算符. 算术运算符 比较(关系)运算符 赋值运算符 逻辑运算符 位运算 ...

  6. hexo与github page搭建博客

    安装 npm i hexo-cli -g hexo init blog cd blog npm install hexo server 发布hexo到github page npm i hexo-de ...

  7. BZOJ2159 Crash的文明世界——树上DP&&第二类Stirling数

    题意 给定一个有 $n$ 个结点的树,设 $S(i)$ 为第 $i$ 个结点的“指标值”,定义为 $S(i)=\sum_{i=1}^{n}dist(i,j)^k$,$dist(i, j)$ 为结点 $ ...

  8. Linux 字符集的查看及修改

    一·查看字符集 字符集在系统中体现形式是一个环境变量,以CentOS6.5为例,其查看当前终端使用字符集的方式可以有以下几种方式: 第一种: [root@Testa-www tmp]# echo $L ...

  9. C++ socket bind() 函数绑定错误

    VS2015编译错误: errorCxxxx: 'initializing' : cannot convert from 'std::_Bind<false,void,SOCKET&,s ...

  10. am335x system upgrade kernel ethernet(四)

    1      Scope of Document This document describes ethernet hardware design and porting KZS8081 to ubo ...