暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle SQL 解析与执行

怪力乱神 2024-01-15
345

一、Oracle SQL 解析

在 Oracle 处理用户提交的 SQL 前,必须完成一个重要的步骤:SQL 解析。Oracle SQL 解析的过程如图所示:


- Oracle SQL 解析主要分为软解析、硬解析、以及软软解析;
- 硬解析需要消耗较多的资源,应当尽量减少硬解析;
- 软解析是性能较高的解析方式,从 Library Cache 缓存中获取 SQL 游标(可能需要 Reload);
- 软软解析是一种特殊的解析方式(不解析),Oracle 将直接从 Session 游标缓存中读取 SQL 游标,是性能最高的解析方式。

1、硬解析

在 SQL 语句第一次执行前,系统需要对该 SQL 进行硬解析操作。Oracle 硬解析主要包括以下规则:

- SQL 语法检查(检验 SQL 语法的正确性),例如:

// 语法检查

SQL> select *;
select *

ORA-00923: FROM keyword not found where expected


可以看到,在 SQL执行前,需要进行 SQL 语法的检查。

- SQL 语义检查(表、列以及关联对象等),通过查询数据字典,确认 SQL 引用的对象是否存在,如下:

SQL> desc emp;
Name     Type              Nullable Default Comments 
-------- ----------------- -------- ------- -------- 
EMPNO    NUMBER(4)                                   
ENAME    VARCHAR2(10 BYTE) Y                         
JOB      VARCHAR2(9 BYTE)  Y                         
MGR      NUMBER(4)         Y                         
HIREDATE DATE              Y                         
SAL      NUMBER(7,2)       Y                         
COMM     NUMBER(7,2)       Y                         
DEPTNO   NUMBER(2)         Y 

SQL> select emp.empno, emp.loc from emp;
select emp.empno, emp.loc from emp

ORA-00904: "EMP"."LOC": invalid identifier


可以看到,在 SQL 执行前,需要进行 SQL 语义的检查。

- SQL 游标的用户访问权限检查,用于验证解析用户是否对 SQL 游标引用的对象拥有足够访问权限:如果有足够权限,则放行解析;否则退出解析环节。
- 解析锁定,保证解析树完整性;
- 优化器选择,系统评估选择优化器(CBO/RBO);
- 产生与优化器匹配的执行计划。

2、软解析

在 SQL 硬解析之前,Oracle 会对用户当前提交的 SQL 进行 Hash 运算,然后将计算得到的 Hash 值与 Library Cache 中 SQL 游标缓存的 Hash 值进行比对。如果能够在 Library Cache 找到与用户提交 SQL 匹配的 SQL 游标缓存(Hash值相同),那么 Oracle 就会重用先前缓存的 SQL 游标而不进行硬解析操作,这个过程就是软解析。

提示:在10g以前,判断 SQL 与 SQL 游标缓存是否匹配是通过 sql_hash_value 来决定的。实际上,在10g以后使用 sql_id 就可以唯一判断了。

Oracle 软解析主要包含如下规则:

- SQL 游标的私有 SQL Area 在 Session PGA 中创建。
- SQL 游标的共享 SQL Area 存放在 Library Cache 中:
   □ 如果新用户发起的 SQL 能够找到相匹配的缓存 SQL 游标,则软解析;
   □ 如果新用户发起的 SQL 不能找到相匹配的缓存 SQL 游标,则硬解析。
- 大多数应用解析一次,执行多次。
- 软解析(硬解析)次数应当尽可能少。

Oracle 软解析前置步骤:在 Oracle 软解析实现前,必须进行用户名称转换和授权验证这2个步骤。
1)用户名称转换:当 Session 用户第一次使用存放在 Library Cache 中的共享游标前,首先需要对该共享游标进行用户名称转换(游标解析主体为其他用户),转换之后才能结合授权验证来重用共享游标进行软解析。
2)授权验证:当用户名称转换和授权验证通过后,还仍然需要对授权进行验证。这是因为如果游标的先前用户最后一次执行该游标后,游标相关对象的授权发送了变化,那么就需要再次对授权进行验证才能实现软解析。

3、软软解析

软软解析也称为不解析,与参数 session_cached_cursors 紧密相关。从某种意义上讲,软软解析也可以看作特殊的软解析。
一旦使用参数 session_cached_cursors 后,Session 用户就可以直接重用缓存在 Session 游标缓存区的 SQL 游标。与此同时,只要 SQL 游标保留在 Session 游标缓存区内,任何关闭 SQL 游标的行为都将被忽略,这样就提高了 SQL 游标的完全重用率,大大减少了 SQL 解析次数,对数据库性能提升产生了深远的影响。

二、Oracle SQL 执行

当 SQL 解析完成后,系统需要将 SQL 提交后台执行,而 SQL 执行最重要的依据就是 SQL 执行计划。

1、SQL 执行计划(树)概览

Oracle 执行计划输出是一棵完整的执行计划树,执行计划(树)的特点有:

- 执行计划中的最高节点是执行计划的起点;
- 执行计划中的每一行代表一行数据源;
- 当2个子 Level 处于同级 Level 时,子 Level 值大的优先执行;
- 执行计划体现了 SQL 执行的逻辑层次和先后顺序。

2、SQL 执行计划(树)与父子 Level

执行计划(树)中包含了父 Level 和子 Level,用于展示执行计划中执行步骤的先后执行顺序。执行计划于父子 Level 关系是:从父 Level(Level0)开始执行,找到所有匹配的分支 Level 递归执行,直到无分支 Level 时结束,如下所示:

SQL> explain plan for
  2  select e.mgr, d.dname, b.sal
  3  from emp e, dept d, bonus b
  4  where e.deptno=d.deptno
  5  and e.ename=b.ename;

Explained


SQL> set lin 100;
Cannot SET LIN

SQL> set pagesize 100;

SQL> select id ||'         '|| parent_id ||'         '|| lpad('  ',2*level-1) || operation || ' ' || options || ' ' || object_name || ' (Cost=' || cost || ')' as "Execute Plan"
  2  from plan_table
  3  start with id=0
  4  connect by prior id=parent_id;

Execute Plan
--------------------------------------------------------------------------------
0                   SELECT STATEMENT   (Cost=7)
1         0            NESTED LOOPS   (Cost=)
2         1              NESTED LOOPS   (Cost=7)
3         2                HASH JOIN   (Cost=6)
4         3                  TABLE ACCESS FULL BONUS (Cost=2)
5         3                  TABLE ACCESS FULL EMP (Cost=3)
6         2                INDEX UNIQUE SCAN PK_DEPT (Cost=0)
7         1              TABLE ACCESS BY INDEX ROWID DEPT (Cost=1)

8 rows selected

通过上述内容可以看到:

1)执行计划从父 Level 0 开始,父 Level 只有一个分支 Level 1,如下所示:

Execute Plan
--------------------------------------------------------------------------------
0                   SELECT STATEMENT   (Cost=7)
1         0            NESTED LOOPS   (Cost=)                                                   -->pid=0,level 1

可以看到,Level 0 只包含 Level 1 这样的 1 个分支 Level。
2)子 Level 1 开始递归,子 Level 1 有 2 个分支 Level 2 和 Level 7,如下:

Execute Plan
--------------------------------------------------------------------------------
0                   SELECT STATEMENT   (Cost=7)
1         0            NESTED LOOPS   (Cost=)
2         1              NESTED LOOPS   (Cost=7)                                               -->pid=1,level 2
......


7         1              TABLE ACCESS BY INDEX ROWID DEPT (Cost=1)         -->pid=1,level 7

可以看到,Level 1 包含 Level 2 和 Level 7 这样的 2 个分支 Level,由于 Level 7 优先级高于 Level 2(pid7 > pid2),因此,Level 7 优先执行。
3)对于优先执行的 Level 7 ,发现没有子 Level ,结束分支;
4)对于与 Level 7 平级的 Level 2 分支,递归遍历发现存在 2 个分支:Level 3 和 Level 6,如下所示:

Execute Plan
--------------------------------------------------------------------------------
0                   SELECT STATEMENT   (Cost=7)
1         0            NESTED LOOPS   (Cost=)
......
3         2                HASH JOIN   (Cost=6)                                                      -->pid=2,level 3
......
6         2                INDEX UNIQUE SCAN PK_DEPT (Cost=0)                     -->pid=2,level 6
......

可以看到,Level 2 包含 Level 3 和 Level 6 这样的 2 个分支 Level,由于 Level 6 优先级高于 Level 3(pid6 > pid3),因此,Level 6 优先执行。
5)对于优先执行的 Level 6 的分支,发现没有子 Level,结束分支;
6)对于与 Level 6 平级的 Level 3 分支,递归遍历发现存在 2 个分支:Level 4 和 Level 5,如下所示:

Execute Plan
--------------------------------------------------------------------------------
0                   SELECT STATEMENT   (Cost=7)
1         0            NESTED LOOPS   (Cost=)
2         1              NESTED LOOPS   (Cost=7)
......
4         3                  TABLE ACCESS FULL BONUS (Cost=2)                       -->pid=3,level 4
5         3                  TABLE ACCESS FULL EMP (Cost=3)                            -->pid=3,level 5

可以看到,Level 3 包含 Level 4 和 Level 5 这样的 2 个分支 Level,由于 Level 5 优先级高于 Level 4(pid5 > pid4),因此,Level 5 优先执行。
7)对于优先执行的 Level 5 的分支,发现没有子 Level,结束分支;
8)对于与 Level 5 平级的 Level 4 分支,发现没有子 Level,结束分支。
至此,实现了执行计划的执行过程。
我们将执行计划父子 Level 的执行过程概括为图 5-3 所示。
其中:
□ “A.B” 格式代表 “父 Level.子Level”,例如1.2 表示 父 Level 1 和 子 Level 2 的执行步骤,如下所示:

2         1              NESTED LOOPS   (Cost=7)                                               -->pid=1,level 2

□ “<<>>” 表示 Level 分支。
不难看出,Oracle 执行计划展示了 SQL 执行的逻辑层次与先后顺序。


技巧:通过执行计划(树)可以看到,Oracle 连接查询最为常见的是两张表的连接查询。换句话说,任意复杂的连接查询都可以简化为基本的两表连接。










「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论