基于OracleSQL优化阅读笔记

Oracle中一些常见的访问B树索引的方法

  1. 索引唯一性扫描
  2. 索引范围扫描
  3. 索引全扫描       -->单块读    NOT NULL约束  自带排序
  4. 索引快速全扫描   -->多块读   无法保证结果的顺序   
  5. 索引跳跃式扫描   -->适用前导列 distinct值不多的情形

 表连接的方法:

1.排序合并连接

      • 步骤如下:  
    1. 首先以目标SQL中指定的谓词条件(如果有的话)去访问T1,然后对访问结果按照表T1中的连接列来排序,排好序后的结果集我们记为1。
    2. 接着以目标SQL中指定的谓词条件(如果有的话)去访问T2,然后对访问结果按照表T2中的连接列来排序,排好序后的结果集我们记为2。
    3. 最后对结果集1和结果集2执行合并操作,从中取出匹配记录来作为排序合并连接的最终执行结果。
      • 优缺点:

通常情况下,排序合并连接的执行效率远不如哈希连接,但前者的使用范围更广,因此哈希连接通常之能用于等值连接条件,而排序合并连接还能用于其他连接条件(例如<、<=、>、>=)。

2.嵌套循环连接

两个表在做表连接时依靠两层嵌套循环(分别为外层循环和内层循环)来得到连接结果集的表连接方法。

      • 步骤如下:
    1. 首先,优化器会按照一定的规则来决定T1表和T2表中谁是驱动表、谁是被驱动表。驱动表用于外层循环,被驱动表用于内层循环。这里假设驱动表是T1,被驱动表是T2。
    2. 接着以目标SQL中指定的谓词条件(如果有的话)去访问驱动表T1,访问驱动表T1后得到的结果集记为1.
    3. 然后遍历结果集1并同时遍历被驱动表T2,即先取出驱动结果集1中的第1条记录,接着遍历被驱动表T2并按照连接条件去判断T2中是否存在匹配的记录,然后再取出驱动结果集1中的第2条记录,按照同样的连接条件再去遍历被驱动表T2并判断T2中是否还存在匹配的记录,直到遍历完驱动结果集1中所有的记录为止。这里的外层循环是指遍历驱动结果集1所对应的循环,内层循环是指遍历被驱动表T2所对应的循环。显然,外层循环所对应的驱动结果集1有多少条记录,遍历被驱动表T2的内层循环就要做多少次,这就是所谓的“嵌套循环“的含义。
      • 优缺点:
    1. 如果驱动表所对应的驱动结果集的记录数较少,同时在被驱动表的连接列上又存在唯一性索引(或者在被驱动表的连接列上存在选择性很好的非唯一性索引),那么此时使用嵌套循环连接的执行效率就会非常高:但如果驱动表所对应的驱动结果集的记录数很多,即便在被驱动表的连接列上存在索引,此时使用嵌套循环连接的执行效率也不会高。
    2. 只要驱动结果集的记录数较少,那就具备了做嵌套循环连接的前提条件,而驱动结果集是在对驱动表应用了目标SQL中指定的谓词条件(如果有的话)后得到的结果集,所以大表也可以作为嵌套循环连接的驱动表,关键看目标SQL中指定的谓词条件(如果有的话)能否将驱动结果集的数据量降下来。
    3. 嵌套循环连接有一个其他方法所没有的一个优点:嵌套循环连接可以实现快速响应,即它可以第一时间先返回已经连接过且满足连接条件的记录,而不必等待所有的连接操作全部做完后才返回连接结果。虽然排序合并连接和哈希连接也可以先返回已经连接过且满足连接条件的记录,而不必等待所有的连接操作都做完,但是它们并不是第一时间返回,因为排序合并连接要等到先排序后做合并操作才能开始返回数据,而哈希连接则要等到驱动结果集所对应的Hash Table全部建完后才能开始返回数据。

3.哈希连接

哈希连接(Hash Join)是一种两个表在做表连接时主要依靠哈希运算来得到连接结果集的连接方法。

      • 步骤如下:
    1. Oracle会根据一堆参数的值来决定Hash Partiton的数量(Hash Bucket组成Hash Partition,Hash Partition组成Hash Table)。
    2. 表T1和T2在施加了目标SQL中指定的谓词条件(如果有的话)后,得到的结果集中数据量较少的那个结果集会被Oracle选为哈希连接的驱动结果集,这里假设T1所对应的结果集的数据量相对较少,记为S;T2所对应的结果集的数据量相对较多,记为B。显然这里S是驱动结果集,B是被驱动结果集。
    3. 接着Oracle会遍历S,读取S中的每一条记录,并对每一条记录按照该记录在表T1中的连接列做哈希运算。这个哈希运算会使用两个内置哈希函数,这两个哈希函数会同时对该连接列计算哈希值,我们把这两个内置哈希函数分别记为hash_func_1和hash_func_2,它们所计算出来的哈希值分为记为hash_value_1和hash_value_2。
    4. 然后oracle会按照hash_value_1的值把相应的S中的对应记录存储在不同的Hash Partiton的不同Hash Bucket里,同时和该记录存储在一起的还有该记录用Hash_func_2计算出来的hash_values_2。注意,存储在Hash Bucket里的记录并不是目标表的完整行记录,只需要存储位于目标SQL中与目标表相关的查询列和连接列就足够了。我们把S所对应的每一个Hash Partiton记为Si。
    5. 在构建Si的同时,Oracle会构建一个位图(BITAMP),这个位图用来标记Si所包含的每一个Hash Bucket是否有记录(即记录数是否大于0)。
    6. 如果S的数据量很大,那么在构建S所对应的Hash Table时,就可能会出现PGA的工作区被填满的情况。这时候Oracle会把工作区中包含记录数最多的Hash Partiton写到磁盘上(TEMP表空间)。接着会继续构建S所对应的Hash Table,在构建的过程中,如果工作区又满了,则Oracle会继续重复上述动作,即挑选包含记录数最多的Hash Partiton并写回到磁盘上。如果要构建的记录所对应的Hash Partition已经事先被Oracle写回磁盘,则此时Oracle就会去磁盘上更新该Hash Partiton,即把该记录和hash_value_2直接加到这个已经位于磁盘上的Hash Partition的相应的Hash Bucket中。注意,极端情况下可能会出现只有某个Hash Partition的部分记录还在内存中,该Hash Partiton的剩余部分和余下的所有Hash Pattiton都已经被写回到磁盘上。
    7. 上述构建S所对应的Hash Table的过程会一直持续下去,直到遍历完S中的所有记录为止。
    8. 接着Oracle会对所有的Si按照它们所包含的记录数来排序,然后把这些已经排好序的Hash Patition按顺序依次且尽可能全部放到内存中(PGA的工作区),当然,如果实在放不下,放不下的那部分Hash Partition还是会位于磁盘上。
    9. 至此Oracle已经处理完S,现在可以开始处理B了。
    10. Oracle会遍历B,读取B中的每一条记录,并按照该记录在表T2中的连接列做哈希运算,这个哈希运算和步骤3中的哈希运算是一摸一样的。即还是会用步骤3中的hash_func_1和hash_func_2,并且也会计算出两个哈希值hash_value_1和hash_value_2。

     。。。。。剩下的看书吧,太多字了

      • 优缺点:
    1. 哈希连接不一定会排序,或者说大多数情况下都不需要排序。
    2. 哈希连接的驱动表所对应的连接列的可选择性应尽可能好,因此这个可选择性会影响对应的Hash Bucket中的记录数,而Hash Buket中的记录数又会直接影响从该Hash Bulket中查找匹配记录的效率。如果一个Hash Bulket里包含的记录数过多,则可能会严重降低所对应哈希连接的执行效率,此时典型的表现就是该哈希连接执行了很长时间都没结束,数据库所在数据库服务器上的cpu占用率很高,但目标SQL所消耗的逻辑读却很低,因为此时大部分时间都耗费在了遍历上述Hash Bucket里的所有记录上,而遍历Hash Bucket里的记录这个动作发生在PGA的工作区里,所以不耗费逻辑读。
    3. 哈希连接只适用于CBO,它也只能用于等值连接条件(即使是哈希反连接,Oracle实际上也是将其转换成了等价的等值连接)。
    4. 哈希连接很适合于小表和大表之间做表连接且连接结果集的记录数较多的情形,特别是在小表的连接列的可选择性非常好的情况下,这时候哈希连接的执行时间就可以近似看作是和全表扫描那个大表所耗费的时间相当。

自我理解:就是先对驱动表(小表)计算好哈希值并存储在内存或磁盘中,再对被驱动表(大表)的每一行计算哈希值,在遍历这个大表,用哈希值找小表中是否有匹配记录。匹配就返回记录。

4.笛卡尔积连接

猜你喜欢

转载自www.cnblogs.com/studyking/p/11595595.html
今日推荐