返回顶部
首页 > 资讯 > 数据库 >Oracle 学习之性能优化(七)join的实现方式
  • 367
分享到

Oracle 学习之性能优化(七)join的实现方式

2024-04-02 19:04:59 367人浏览 独家记忆
摘要

  本文讨论一下join技术背后的机制。我们知道常用的表连接有如下几种笛卡尔连接内连接左外连接右外连接全连接这些sql的写法,想必大家都很清楚了,那么这些连接的数据访问是如何实现的呢?nested

  本文讨论一下join技术背后的机制。我们知道常用的表连接有如下几种

  • 笛卡尔连接

  • 内连接

  • 左外连接

  • 右外连接

  • 全连接

这些sql的写法,想必大家都很清楚了,那么这些连接的数据访问是如何实现的呢?

  • nested loop

我们看如下查询

SQL> alter session set optimizer_mode=rule;

Session altered.

SQL> select ename,dname from emp,dept where emp.deptno=dept.deptno;

14 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 3625962092

------------------------------------------------
| Id  | Operation		     | Name    |
------------------------------------------------
|   0 | SELECT STATEMENT	     |	       |
|   1 |  NESTED LOOPS		     |	       |
|   2 |   NESTED LOOPS		     |	       |
|   3 |    TABLE ACCESS FULL	     | EMP     |
|*  4 |    INDEX UNIQUE SCAN	     | PK_DEPT |
|   5 |   TABLE ACCESS BY INDEX ROWID| DEPT    |
------------------------------------------------

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

   4 - access("EMP"."DEPTNO"="DEPT"."DEPTNO")

根据我们之前讲的执行计划解读,本查询是这样实现的:

  1. 全表扫描emp表(非阻塞扫描,并不是将数据全部取出,才执行下一步)。

  2. 将emp中的数据逐条取出,通过索引PK_DEPT查询出索引中的rowid,结果集变成(ename,rowid)

  3. 将2生成的结果集逐条取出,通过rowid去访问dept表,结果集变成(ename,dname)

  4. 将结果集返回。

这种以循环的方式取出数据的join实现方式就叫嵌套循环。

此计划可以用如下逻辑伪代码实现

for y in (for x in (select * from emp)loop
         index lookup the rowid for x.deptno
         output joined record(ename,dept.rowid)
       end loop)loop
 select * from dept where rowid=y.rowid
 output joined record(ename,dname)
end loop

我们把emp表称之为驱动表(注驱动表与from子句的表顺序无关,主要看执行计划)。

此种连接方式,适用于驱动表返回数据比较少,并且被驱动表dept上deptno列有索引。如果查询返回n行,那么dept表将被扫描n次。此连接擅长于从结果集中迅速取出第一行。

  • Hash Join

  Hash Join适合处理大型结果集,优化器选择两个表或者源数据中比较小的,使用join key在内存中建立一个hash table。然后扫描大表,并探查hash表,去发现匹配的记录。
  小表称为驱动表,大表称为探查表

 Oracle 学习之性能优化(七)join的实现方式


当hash table能全部放到内存中,此种情况最好。如果内存中放不下hash table,优化器将hash table分区,超出内存范围的分区将被写到临时表空间中。

我们分两种情况讨论hash join的实现

  1. hash table 全部在内存里

  hash table是oracle根据join key利用一个hash函数将小表分割成多个bucket。hash table建立完成后,Oracle去扫描大表,并且采用相同的hash算法,将读入的数据也分割成多个bucket。bucket与bucket之间进行join运算,返回结果。直到大表读完为止。

2. hash table 不能全部放到内存中

  这种情况有点麻烦,当Oracle发现内存无法完全存放小表,Oracle在构造hash table时,将小表进行分区,每个分区中再构造bucket 。当内存写满时,Oracle将内存中最大的分区写到tempfile中。用这个方法,直到小表hash table构造完成。此时,hashtable一部分数据在内存,一部分数据在tempfile。

  当Oracle去扫描大表时,如果扫描的行通过hash在内存中能找到结果,就匹配成功。如果不能命中,则采用与hash table相同的分区方式,先将数据写入tempfile中。当大表全部扫描完毕,hash table内存中的部分已全部匹配完。此时依次将tempfile中的分区加载到内存中。重新扫描大表临时存在tempfile中的相应分区加以匹配。直到数据全部处理完。

SQL> insert into big_emp select * from big_emp;
SQL> insert into big_emp select * from big_emp;#重复执行多次
SQL> /

458752 rows created.

SQL> create table dept_new as select * from dept;

Table created.

SQL> set autot traceonly
SQL> select * from big_emp a,dept_new b where a.deptno=b.deptno;

917504 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 1925493178

-------------------------------------------------------------------------------
| Id  | Operation	   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |	      |   917K|    54M|  1490	(2)| 00:00:18 |
|*  1 |  HASH JOIN	   |	      |   917K|    54M|  1490	(2)| 00:00:18 |
|   2 |   TABLE ACCESS FULL| DEPT_NEW |     4 |   120 |     3	(0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| BIG_EMP  |   917K|    28M|  1482	(1)| 00:00:18 |
-------------------------------------------------------------------------------

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

   1 - access("A"."DEPTNO"="B"."DEPTNO")

Note
-----
   - dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
	  4  recursive calls
	  1  db block gets
      66338  consistent gets
	  0  physical reads
	  0  redo size
   62512398  bytes sent via SQL*Net to client
     673349  bytes received via SQL*Net from client
      61168  SQL*Net roundtrips to/from client
	  0  sorts (memory)
	  0  sorts (disk)
     917504  rows processed


  • Sort Merge Joins

   排序合并连接与嵌套循环和散列连接都不同,排序合并连接没有驱动表的概念。简言之,排序合并将依次处理排序第一个输入集,排序第二个输入集,然后合并结果。排序合并通常不如散列高效,因为两个结果集都需要排序,而散列连接在数据输出前,只需处理一个结果集。排序合并通常在非等值连接中有效。即连接条件不是一个等式而是范围比较(<或者>=). 或者是两个表的数据已经排好序啦。

我们看如下例子

SQL> set linesize 200 pagesize 200
SQL> set autot traceonly
SQL> select a.ename,b.ename,a.hiredate,b.hiredate
  2  from emp a,emp b
  3  where a.empno<>b.empno and a.hiredate<b.hiredate;

90 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 3733349388

-----------------------------------------------------------------------------
| Id  | Operation	     | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |	    |	 84 |  3024 |	  8  (25)| 00:00:01 |
|   1 |  MERGE JOIN	     |	    |	 84 |  3024 |	  8  (25)| 00:00:01 |
|   2 |   SORT JOIN	     |	    |	 14 |	252 |	  4  (25)| 00:00:01 |
|   3 |    TABLE ACCESS FULL | EMP  |	 14 |	252 |	  3   (0)| 00:00:01 |
|*  4 |   FILTER	     |	    |	    |	    |		 |	    |
|*  5 |    SORT JOIN	     |	    |	 14 |	252 |	  4  (25)| 00:00:01 |
|   6 |     TABLE ACCESS FULL| EMP  |	 14 |	252 |	  3   (0)| 00:00:01 |
-----------------------------------------------------------------------------

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

   4 - filter("A"."EMPNO"<>"B"."EMPNO")
   5 - access("A"."HIREDATE"<"B"."HIREDATE")
       filter("A"."HIREDATE"<"B"."HIREDATE")


Statistics
----------------------------------------------------------
	  1  recursive calls
	  0  db block gets
	 12  consistent gets
	  0  physical reads
	  0  redo size
       3500  bytes sent via SQL*Net to client
	578  bytes received via SQL*Net from client
	  7  SQL*Net roundtrips to/from client
	  2  sorts (memory)
	  0  sorts (disk)
	 90  rows processed

再看一个等值连接的

SQL> select ename,dname from emp a,dept b where a.deptno=b.deptno;

14 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 844388907

----------------------------------------------------------------------------------------
| Id  | Operation		     | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	     |	       |    14 |   308 |     6	(17)| 00:00:01 |
|   1 |  MERGE JOIN		     |	       |    14 |   308 |     6	(17)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     4 |    52 |     2	 (0)| 00:00:01 |
|   3 |    INDEX FULL SCAN	     | PK_DEPT |     4 |       |     1	 (0)| 00:00:01 |
|*  4 |   SORT JOIN		     |	       |    14 |   126 |     4	(25)| 00:00:01 |
|   5 |    TABLE ACCESS FULL	     | EMP     |    14 |   126 |     3	 (0)| 00:00:01 |
----------------------------------------------------------------------------------------

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

   4 - access("A"."DEPTNO"="B"."DEPTNO")
       filter("A"."DEPTNO"="B"."DEPTNO")


Statistics
----------------------------------------------------------
	  1  recursive calls
	  0  db block gets
	 10  consistent gets
	  0  physical reads
	  0  redo size
	819  bytes sent via SQL*Net to client
	523  bytes received via SQL*Net from client
	  2  SQL*Net roundtrips to/from client
	  1  sorts (memory)
	  0  sorts (disk)
	 14  rows processed

 

您可能感兴趣的文档:

--结束END--

本文标题: Oracle 学习之性能优化(七)join的实现方式

本文链接: https://lsjlt.com/news/52677.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Oracle 学习之性能优化(七)join的实现方式
      本文讨论一下join技术背后的机制。我们知道常用的表连接有如下几种笛卡尔连接内连接左外连接右外连接全连接这些sql的写法,想必大家都很清楚了,那么这些连接的数据访问是如何实现的呢?nested...
    99+
    2024-04-02
  • Oracle 学习之性能优化(八)优化器
       我们知道,在sql语句解析的过程中,有一个过程叫优化。Oracle中有一个叫优化器的组件,专门来处理sql的优化。在考虑查询条件和对象引用的许多相关因素后,优化器能确定出执行SQL语句最有效...
    99+
    2024-04-02
  • Oracle 学习之性能优化(二)游标
      游标是cursor的中文翻译,那么到底什么是cursor呢?Oracle Concept中是这样描述的:  When an application issues a SQL stat...
    99+
    2024-04-02
  • Oracle 学习之 性能优化(十四) 内存
     Oracle数据库包含了如下基本内存组件System global area (SGA)The SGA is a group of shared memory structures, known...
    99+
    2024-04-02
  • Oracle 学习之性能优化(九)使用hint
            基于代价的优化器是很聪明的,在绝大多数情况下它会选择正确的优化器,减轻了DBA的负担。但有时它也聪明反被聪明误,选择了很差的执行计划,使某个语句的执行...
    99+
    2024-04-02
  • Oracle 学习之性能优化(十一)物化视图
      物化视图主要用于预先计算并保存表连接或聚集等耗时较多的操作的结果,这样,在执行查询时,就可以避免进行这些耗时的操作,而从快速的得到结果。在数据仓库中,还经常使用查询重写(query rewri...
    99+
    2024-04-02
  • Oracle 学习之 性能优化(十二) 分区表
    一、分区概念   Oracle允许将表、索引、索引组织表细分成更小的片,每个片我们称之为分区。分区有其自己的名字和存储参数。  如下图:每行数据只能属于一个分区,分区键决定数据...
    99+
    2024-04-02
  • Oracle 学习之 性能优化(十五) ASH、ADDM、AWR
    ASH(Active Session History)  ASH以V$SESSION为基础,每秒采样一次,记录活动会话等待的事件。不活动的会话不会采样,采样工作由新引入的后台进程MMNL来完成。A...
    99+
    2024-04-02
  • Oracle 学习之性能优化(三)绑定变量
      根据Oracle 学习之性能优化(二)游标中的描述,我们知道如下两条语句是不共享的。select * from emp where&...
    99+
    2024-04-02
  • Oracle 学习之性能优化(六)访问路径
      访问路径是指Oracle找到用户需要的数据的方法,这些方法很少,包括:声名狼藉的全表扫描--人们不惜一切视图避免的(曲解的)访问路径。各种类型的索引扫描--这是人们感觉良好的访问路径(多数情况...
    99+
    2024-04-02
  • Oracle 学习之性能优化(一)SQL语句处理
      当向Oracle提交一个sql命令时,Oracle到底做了哪些事情?对这个问题有很好的理解,能帮助你更好的分析sql语句的优化。  执行一条sql语句从开始到结束,需要经历4个步骤:...
    99+
    2024-04-02
  • Oracle 学习之性能优化(四)收集统计信息
     emp表有如下数据。SQL> select ename,deptno from emp; ENAME   &n...
    99+
    2024-04-02
  • Oracle学习篇之SQL语句的优化
    Oracle学习篇之SQL语句的优化①在使用SELECT语句查询时,不要用“*”代替所有列名,因为这样的写法对Oracle系统来说会存在解析的动态问题。Oracle系统会通过查询数据字典来将“*”转...
    99+
    2024-04-02
  • MySQL查询性能优化七种方式索引潜水
    目录前言: 有读者可能会一脸懵? 啥是索引潜水? 你给起的名字的吗?有没有索引蛙泳? 这个名字还真不是我起的,今天要讲的知识点就叫索引潜水(Index dive) 。 先要...
    99+
    2022-11-13
    MySQL查询性能优化 MySQL索引潜水
  • Vue2.x 项目性能优化之代码优化的实现
    目录1 v-if 和 v-show 的使用 2 computed 和 watch 区分使用 3 v-for 遍历必须为 item 添加 key,且避免同时使用 v-if 4 纯显示长...
    99+
    2024-04-02
  • Golang 技术性能优化中如何实现分布式性能优化?
    如何实现 golang 分布式性能优化?并发编程: 利用 goroutine 并行执行任务。分布式锁: 使用互斥锁防止并发操作导致数据不一致。分布式缓存: 使用 memcached 减少...
    99+
    2024-05-12
    golang 性能优化 git
  • Vite性能优化之分包策略的实现
    目录为什么需要分包策略?浏览器的缓存策略浏览器缓存策略的不足分包策略的实现为什么需要分包策略? 还是举个例子,来回忆一下吧,什么呢?比如说,我使用了 loadsh.js 的东西,那么...
    99+
    2023-05-19
    Vite 分包策略 Vite 分包
  • Android 分析实现性能优化之启动速度优化
    目录启动方式冷启动(启动优化目标)热启动温启动启动流程中可优化的环节检测工具启动时间检测Logcat Displayedadb 命令统计CPU profileAPI level &g...
    99+
    2024-04-02
  • React性能优化的实现方法详解
    目录前言遍历视图key使用React.memo缓存组件React.useCallback让函数保持相同的引用避免使用内联对象使用React.useMemo缓存计算结果或者组件使用Re...
    99+
    2023-01-30
    React性能优化方法 React性能调优
  • 怎么实现优化ORACLE数据库性能
    怎么实现优化ORACLE数据库性能?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。ORACLE数据库的优化方式和MYSQL等很大的区别,今天...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作