zoukankan      html  css  js  c++  java
  • Oracle 表连接 介绍

    表的连接

    表的连接是指在一个SQL语句中通过表与表之间的关联,从一个或多个表检索出相关的数据。如果一个SQL语句的关联表超过两个,
    那么连接的顺序如何呢?ORACLE首先连接其中的两个表,产生一个结果集;然后将产生的结果集与下一个表再进行关联;继续这
    个过程,直到所有的表都连接完成;最后产生所需的数据。

    ORACLE 从6的版本开始,优化器使用4种不同的表的连接方式:

    1 嵌套循环连接(NESTED LOOP JOIN)
    2 群集连接 (CLUSTER JOIN)
    3 排序合并连接(SORT MERGE JOIN)
    4 笛卡尔连接 (CARTESIAN JOIN)

    ORACLE 7.3中,新增加了

    5 哈希连接(HASH JOIN)。

    在ORACLE 8中,新增加了

    6 索引连接(INDEX JOIN)。

    这六种连接方式都有其各自技术特点,在一定的条件下,可以充分发挥高效的性能。但是也都有其局限性,如果使用不当,不仅不
    能提高效率,反而会严重影响系统的性能。因此,深入地探讨连接方式的内部 运行机制对于性能优化是必要的。


    1 嵌套循环连接

    嵌套循环连接的内部处理的流程:

    1) Oracle 优化器根据基于规则RBO或基于成本CBO的原则,选择两个表中的一个作为驱动表,并指定其为外部表。
    2) Oracle 优化器再将另外一个表指定为内部表。
    3) Oracle从外部表中读取第一行,然后和内部表中的数据逐一进行对比,所有匹配的记录放在结果集中。
    4) Oracle读取外部表中的第二行,再和内部表中的数据逐一进行对比,所有匹配的记录添加到结果集中。
    5) 重复上述步骤,直到外部表中的所有纪录全部处理完。
    6) 最后产生满足要求的结果集。


    使用嵌套循环连接是一种从结果集中提取第一批记录最快速的方法。在驱动行源表(就是正在查找的记录,外部表)较小、或者内部行
    源表已连接的列有惟一的索引或高度可选的非惟一索引时, 嵌套循环连接效果是比较理想的。嵌套循环连接比其他连接方法有优势,它
    可以快速地从结果集中提取第一批记录,而不用等待整个结果集完全确定下来。这样,在理想情况下,终端用户就可以通过查询屏幕查看
    第一批记录,而在同时读取其他记录。不管如何定义连接的条件或者模式,任何两个行记录源都可以使用嵌套循环连接,所以嵌套循环连
    接是非常灵活的。

    然而,

    如果内部行源表(读取的第二张表)已连接的列上不包含索引,或者索引不是高度可选时,嵌套循环连接效率是很低的。
    如果驱动表的记录非常庞大时,其他的连接方法可能更加有效。

    可以通过在SQL语句中添加HINTS(use_nl),强制ORACLE优化器产生嵌套循环连接的执行计划。


    2 群集连接(CLUSTER JOIN)

    群集连接实际上是嵌套循环连接的一种特例。如果所连接的两张源表是群集中的表,即两张表属于同一个段(SEGMENT),
    那么ORACLE能够使用群集连接。处理的过程是:ORACLE从第一张行源表中读取第一行,然后在第二张行源表中使用CLUSTER索
    引查找能够匹配到的纪录;继续上面的步骤处理行源表中的第二行,直到所有的记录全部处理完。

    群集连接的效率极高,因为两个参加连接的行源表实际上处于同一个物理块上。但是,群集连接也有其限制,没有群集的两
    个表不可能用群集连接。所以,群集连接实际上很少使用。


     

    3 排序合并连接(SORT MERGE JOIN)

    排序合并连接内部处理的流程:
    1) 优化器判断第一个行源表是否已经排序,如果已经排序,则到第3步,否则到第2步。
    2) 第一个源表排序
    3) 优化器判断第二个行源表是否已经排序,如果已经排序,则到第5步,否则到第4步。
    4) 第二个源表排序
    5) 已经排过序的两个源表进行合并操作,并生成最终的结果集。

    在缺乏数据的选择性或者可用的索引时,或者两个行源表都过于庞大(所选的数据超过表记录数的5%)时,排序合并连接将比嵌套
    循环连更加高效。排列合并连接需要比较大的临时内存块,以用于排序,这将导致在临时表空间占用更多的内存和磁盘I/O。

    可以通过在SQL语句中添加HINTS(use_merge),强制ORACLE优化器产生排序合并连接的执行计划。


    4 笛卡尔连接 (CARTESIAN JOIN)

    笛卡尔连接是指在sql语句中没有写出表连接的条件,优化器把第一个表的每一条记录和第二个表的所有纪录相连接。如果第
    一个表的纪录数为m, 第二个表的纪录数为m,则会产生m*n条纪录数。

    由于笛卡尔连接会导致性能很差的SQL,因此一般也很少用到。


    5 哈希连接(Hash Join)

    Hash Join 概述
    Hash join 算法的一个基本思想就是根据小的row sources(称作build input我们记较小的表为S ,较大的表为B)建立一个可以
    存在于 hash area内存中的hash table ,然后用大的 row sources(称作probe input) 来探测前面所建的hash table。如果
    hash area 内存不够大, hash table就无法完全存放在hash area内存中。针对这种情况,Oracle在连接键利用一个hash函数将
    build input 和 probe input 分割成多个不相连的分区(分别记作 Si 和 Bi ),这个阶段叫做分区阶段;然后各自相应的分区,
    即 Si 和 Bi 再做 Hash join ,这个阶段叫做 join 阶段。

    如果在分区后,针对某个分区所建的hash table还是太大的话,oracle就采用nested-loops hash join. 所谓的nested-loops hash join就是对部分 Si 建立 hash table ,然后读取所有的 Bi 与所建的 hash table 做连接,然后再对剩余的 Si 建立 hash table ,再
    将所有的 Bi 与所建的 hash table 做连接,直至所有的 Si 都连接完了。

    Hash Join 算法有一个限制,就是它是在假设两张表在连接键上是均匀的,也就是说每个分区拥有差不多的数据。但是实际当中数据都是不
    均匀的,为了很好地解决这个问题, oracle 引进了几种技术,位图向量过滤、角色互换、柱状图,这些术语的具体意义会在后面详细介绍。

     
    Hash Join 原理
    我们用一个例子来解释 Hash Join 算法的原理,以及上述所提到的术语。

    考虑以下两个数据集。


    S={1,1,1,3,3,4,4,4,4,5,8,8,8,8,10}
    B={0,0,1,1,1,1,2,2,2,2,2,2,3,8,9,9,9,10,10,11}


    oracle优化器根据统计信息,Hash Join 的第一步就是判定小表(即 build input )是否能完全存放在 hash area 内存中。如果能完全存放在内存中,则在内存中建立 hash table ,这是最简单的 hash join 。


    如果不能全部存放在内存中,则 build input 必须分区。分区的个数叫做 fan-out 。 Fan-out 是由 hash_area_size 和 cluster size 来决定的。其中 cluster size 等于 db_block_size * hash_multiblock_io_count , hash_multiblock_io_count 在 oracle9i 中是隐含参数。这里需要注意的是 fan-out 并不是 build input 的大小 /hash_ara_size ,也就是说 oracle 决定的分区大小有可能还是不能完全存放在 hash area 内存中。大的 fan-out 导致许多小的分区,影响性能,而小的 fan-out 导致少数的大的分区,以至于每个分区不能全部存放在内存中,这也影响 hash join 的性能。

    Oracle 采用内部一个 hash 函数作用于连接键上,将 S 和 B 分割成多个分区,在这里我们假设这个 hash 函数为求余函数,即 Mod(join_column_value,10) 。这样产生十个分区,如下表。

    分区

    B0

    B1

    B2

    B3

    B4

    B5

    B6

    B7

    B8

    B9

    0,0,10,10

    1,1,1,1,11

    2,2,2,2,2,2

    3

    NULL

    NULL

    NULL

    NULL

    8

    9,9,9

    S0

    10

    S1

    1,1,1

    S2

    Null

    S3

    3,3

    S4

    4,4,4,4

    S5

    5

    S6

    NULL

    S7

    NULL

    S8

    8,8,8,8

    S9

    NULL


    经过这样的分区之后,只需要相应的分区之间做 join 即可(也就是所谓的 partition pairs ),如果有一个分区为 NULL 的话,则相应的分区 join 即可忽略。

     
    在将 S 表读入内存分区时,oracle将记录连接键的唯一值,构建成所谓的位图向量,它需要占 hash area 内存的 5% 左右。在这里即为 {1,3,4,5,8,10} 。


    当对 B 表进行分区时,将每一个连接键上的值与位图向量相比较,如果不在其中,则将其记录丢弃。在我们这个例子中, B 表中以下数据将被丢弃 {0,0,2,2,2,2,2,2,9,9,9,9,9} 。这个过程就是位图向量过滤。


    当 S1,B1 做完连接后,接着对 Si,Bi 进行连接,这里 oracle 将比较两个分区,选取小的那个做 build input ,就是动态角色互换,这个动态角色互换发生在除第一对分区以外的分区上面。

    Hash Join 算法

    第 1 步:判定小表是否能够全部存放在 hash area 内存中,如果可以,则做内存 hash join 。如果不行,转第二步。
    第 2 步:决定 fan-out 数。
       (Number of Partitions) * C<= Favm *M
        其中 C 为 Cluster size ,
    其值为 DB_BLOCK_SIZE*HASH_MULTIBLOCK_IO_COUNT ; Favm 为 hash area 内存可以使用的百分比,一般为 0.8 左右; M 为 Hash_area_size 的大小。

    第 3 步:读取部分小表 S ,采用内部 hash 函数 ( 这里称为 hash_fun_1) ,将连接键值映射至某个分区,同时采用 hash_fun_2 函数对连接键值产生另外一个 hash 值,这个 hash 值用于创建 hash table 用,并且与连接键值存放在一起。


    第 4 步:对 build input 建立位图向量。
    第 5 步:如果内存中没有空间了,则将分区写至磁盘上。
    第 6 步:读取小表 S 的剩余部分,重复第三步,直至小表 S 全部读完。
    第 7 步:将分区按大小排序,选取几个分区建立 hash table( 这里选取分区的原则是使选取的数据最多 ) 。
    第 8 步:根据前面用 hash_fun_2 函数计算好的 hash 值,建立 hash table 。
    第 9 步:读取表 B ,采用位图向量进行位图向量过滤。
    第 10 步:对通过过滤的数据采用 hash_fun_1 函数将数据映射到相应的分区中去,并计算 hash_fun_2 的 hash 值。
    第 11 步:如果所落的分区在内存中,则将前面通过 hash_fun_2 函数计算所得的 hash 值与内存中已存在的 hash table 做连接, 将结果写致磁盘上。 如果所落的分区不在内存中,则将相应的值与表 S 相应的分区放在一起。


    第 12 步:继续读取表 B ,重复第 9 步,直至表 B 读取完毕。
    第 13 步:读取相应的 (Si,Bi) 做 hash 连接。在这里会发生动态角色互换。
    第 14 步:如果分区过后,最小的分区也比内存大,则发生 nested- loop hash join 。


    Hash Join 的成本

    1. In-Memory Hash Join
     
      Cost(HJ)=Read(S)+ build hash table in memory(CPU)+Read(B) + Perform In memory Join(CPU)
       忽略 cpu 的时间,则
      Cost(HJ)=Read(S)+Read(B)

    2. On-Disk Hash Join
    根据上述的步骤描述,我们可以看出

    Cost(HJ)=Cost(HJ1)+Cost(HJ2)

    其中 Cost(HJ1) 的成本就是扫描 S,B 表,并将无法放在内存上的部分写回磁盘,对应前面第 2 步至第 12 步。
    Cost(HJ2) 即为做 nested-loop hash join 的成本,对应前面的第 13 步至第 14 步。

    其中 Cost(HJ1) 近似等于 Read(S)+Read(B)+Write((S-M)+(B-B*M/S)) 。

    因为在做 nested-loop hash join 时,对每一 chunk 的 build input ,都需要读取整个 probe input ,因此
    Cost(HJ2) 近似等于 Read((S-M)+n*(B-B*M/S))

    其中 n 是 nested-loop hash join 需要循环的次数。 n=(S/F)/M

    一般情况下,如果 n 大于 10 的话, hash join 的性能将大大下降。从 n 的计算公式可以看出, n 与 Fan-out 成反比例,
    提高 fan-out ,可以降低 n 。当 hash_area_size 是固定时,可以降低 cluster size 来提高 fan-out 。

    从这里我们可以看出,提高 hash_multiblock_io_count 参数的值并不一定提高 hash join 的性能。


    当连接的两个表是用等值连接并且表的数据量比较大时,优化器才可能采用哈希连接。哈希连接是基于CBO的。只有在数据库
    初始化参数HASH_JOIN_ENABLED设为True,并且为参数PGA_AGGREGATE_TARGET设置了一个足够大的值的时候,Oracle才会使用哈
    希边连接。HASH_AREA_SIZE是向下兼容的参数,但在Oracle9i之前的版本中应当使用HASH_AREA_SIZE。当使用ORDERED提示时,
    FROM子句中的第一张表将用于建立哈希表。

    当缺少有用的索引时,哈希连接比嵌套循环连接更加有效。哈希连接也可能比嵌套循环连接更快,因为处理内存中的哈希表
    比检索B_树索引更加迅速。 Hash Join 适合于小表与大表连接、返回大型结果集的连接。

    深入地理解和掌握oracle的表连接对于优化数据库的性能至关重要。由于优化器选择方式的不同,以及统计信息的缺失或统
    计信息的不准确,ORACLE自动选择的表连接方式不一定是最优的。当SQL语句的执行效率很低时,可通过auto trace对执行计
    划进行跟踪和分析。当出现多表连接时,需要仔细分析是否有更佳的连接条件。根据系统的特点,必要时可以在SQL中添加
    HINTS,从而改变SQL的执行计划,从而达到性能优化的目的。

    6 索引连接

    如果一组已存在的索引包含了查询所需要的所有信息,那么优化器将在索引中有选择地生成一组哈希表。可通过范围或者快
    速全局扫描访问到每一个索引,而选择何种扫描方式取决于WHERE子句中的可有条件。在一张表有大量的列,而您只想访问有
    限的列时,这种方法非常有效。WHERE子句约束条件越多,执行速度越快。因为优化器在评估执行查询的优化路径时,将把约
    束条件作为选项看待。您必须在合适的列(那些满足整个查询的列)上建立索引,这样可以确保优化器将索引连接作为可选
    项之一。这个任务通常牵涉到在没有索引,或者以前没有建立联合索引的列上增加索引。相对于快速全局扫描,连接索引的

    优势在于:快速全局扫描只有一个单一索引满足整个查询;索引连接可以有多个索引满足整个查询。

  • 相关阅读:
    让计算成为人类的能力,让数据变成世界的财富。今天更多人成了计算和数据的探索者和追梦者,阿里云也是其中之一。
    在巴山小站,我看到一个小男孩和小女孩共同拿着一部手机,在小站的一角蹭网,这是附近唯一能连上Wi-Fi的地方。那一幕对我内心的震撼,无法用言语形容,这就是互联网版的凿壁借光,这一缕光对这些还没有走出大山的孩子而言,是了解世界的窗户,也把他们的梦想和世界连在了一起。
    安存电子951335电话语录语音平台正式上线,你对自己想要录音的电话,可以拨打951335+对方的电话号码,录音内容就会保存在阿里云提供的云存储上,只要有互联网的地方就可以收听、下载及举证应用。
    阿里金融成立,从第一天开始,就使用阿里云的云计算平台作为运算淘宝和支付宝数据的计算后台。阿里小贷不需要抵押,纯信用贷款,目前已经能做到3分钟提交申请,1秒批准,0人工干预。数据成为新的信用。
    杭州成为第一个搭建数据大脑的城市,上接来自城市各路的数据,下接执行系统。这个城市大脑要帮助打通数据的静脉和筋脉,让过去沉淀淤积在一起的数据互通起来,让城市的“眼”摄像头和“手”(交通指挥)的动作协调起来。
    城市大脑的建设代表了杭州的城市管理理念,杭州要为中国和世界探索用云计算和大数据解决城市发展问题的方法。
    云栖小镇不是“镇”,就像中关村不是“村”。小镇是一个符号,就像起建于50年前的硅谷的“谷”,和100年前爱迪生所在的门洛公园。
    美国新泽西州,也有一个Menlo Park——门洛公园,尽管不太为人熟知,但爱迪生那里的一个发明却点亮了全世界。
    2013年1月,我们和华通合作在杭州转塘云计算园区建立了第一个数据中心,随后这个园区被我们称为云栖小镇。我们和园区管委会达成了一个共同目标,就是让云栖小镇成为创业创新的第一基地。
    客户教会了我们非常多的东西,甚至改变了我的世界观。是开发者、客户真正在教我们怎么做云计算。
  • 原文地址:https://www.cnblogs.com/sopost/p/2369488.html
Copyright © 2011-2022 走看看