Oracle 里的优化器

news2024/11/17 21:25:53

对于关系型数据库而言,优化器是最核心的部分。主要是因为优化器负责解析SQL。而大家都是通过SQL来访问存储在数据库中的数据。故此,优化器的好坏直接决定该关系型数据库的强弱。

同时,想要做好SQL优化,就必须深入了解优化器。这是基础。

1.什么是优化器(Optimizer)?

优化器是Oracle数据库中内置的一个核心子系统,可以理解为Oracle数据库中的一个核心模块或者一个核心功能组件

2.优化器的目的是什么?

优化器的目的是按照一定的判断原则来得到它认为的目标SQL在当前情形下最高效的执行路径(Access Path),优化器的目的就是为了得到目标SQL的执行计划

3.优化器有几种类型?

依据选择执行计划时所用的判断原则,Oracle数据库里的优化器又分为RBO和CBO这两种类型。RBO 是Rule-Based Optimizer(基于规则的优化器的缩写。

CBO是Cost-Based Optimizer(基于成本的优化器)的缩写。

4.当得到目标SQL的执行计划时,RBO和CBO各自使用的判断原则是什么?

在得到目标SQL的执行计划时,

RBO所用的判断原则为一组内置的规则,这些规则是硬编码在Oracle数据库的代码中的,RBO会根据这些规则从目标SQL诸多可能的执行路径中选择一条来作为其执行计划

CBO所用的判断原则为成本,CBO会从目标SQL诸多可能的执行路径中选择成本值最小的一条来作为其执行计划,各个执行路径的成本值是根据目标SQL语句所涉及的表、索引、列等相关对象的统计信息计算出来的

5.Oracle数据库里SQL语句的执行过程是怎样的?

5.基于规则的优化器(RBO)的工作原理是什么?

基于规则的优化器(RBO)通过硬编码在Oracle数据库代码中的一系列固定的规则,来决定目标SQL的执行计划。具体来说就是这样:Oracle会在代码里事先给各种类型的执行路径定一个等级,一共有15个等级,从等级1到等级15。并且Oracle会认为等级值低的执行路径的执行效率会比等级值高的执行效率要高,也就是说在RBO的眼里,等级1所对应的执行路径的执行效率最高,等级15所对应的执行路径的执行效率最低。在决定目标SQL的执行计划时,如果可能的执行路径不止一条,则RBO就会从该SQL诸多可能的执行路径中选择一条等级值最低的执行路径来作为其执行计划。

6.RBO是一种适用于OLTP类型SQL语句的优化器,在这样的前提条件下,大家来猜一猜RBO的等级1和等级15所对应的执行路径分别是什么?

对于OLTP类型的SQL语句而言,显然通过ROWID来访问是效率最高的方式,而通过全表扫描来访问则是效率最低的方式。RBO内置的等级1所对应的执行路径就是“single row by rowid(通过rowid来访问单行数据)”,而等级15所对应的执行路径则是“full table scan(全表扫描)”。

从Oracle 10g开始,RBO已不再被Oracle支持,但RBO的相关实现代码并没有从Oracle数据库的代码中移除,这意味着即使是在Oracle 11gR2中,我们依然可以通过修改优化器模式或使用RULE Hint来继续使用RBO

7.相比于CBO,RBO有哪些缺陷?

  • 在使用RBO的情况下,执行计划一旦出了问题,很难对其做调整
  • 如果使用了RBO,则目标SQL的写法,甚至是目标SQL中所涉及的各个对象在该SQL文本中出现的先后顺序,都可能会影响RBO对于该SQL执行计划的选择。
  • Oracle数据库中很多很好的特性、功能均不能在RBO下使用,因为它们均不被RBO所支持。

8. Oracle数据库中有哪些特性、功能不能被RBO所支持?

  • 目标SQL中涉及的对象有IOT(Index Organized Table)。
  • 目标SQL中涉及的对象有分区表。
  • 使用了并行查询或者并行DML。
  • 使用了星型连接。
  • 使用了哈希连接。
  • 使用了索引快速全扫描。
  • 使用了函数索引。

9. 在使用RBO的情况下一旦RBO选择的执行计划并不是当前情形下最优的执行计划应该如何对其做调整呢?

很难对RBO选择的执行计划做调整的,其中非常关键的一个原因就是不能使用Hint,因为如果在目标SQL中使用了Hint,就意味着自动启用了CBO,即Oracle会以CBO来解析含Hint的目标SQL。这里仅有两个例外,就是RULE Hint和DRIVING_SITE Hint它们可以在RBO下使用并且不自动启用CBO。

10.是不是在使用RBO的情况下就没办法对执行计划做调整了?

当然不是这样,只是这种情况下我们的调整手段会非常有限。其中的一种可行的方法就是等价改写目标SQL,例如在目标SQL的where条件中对NUMBER或DATE类型的列加上0(如果是VARCHAR2或CHAR类型,可以加上一个空字符,例如 ||''),这样就可以让原本可以走的索引现在走不了对于包含多表连接的目标SQL而言,这种改变甚至可以影响表连接的顺序,进而就可以实现在使用RBO的情况下对该目标SQL的执行计划做调整的目的

11.RBO会从目标SQL诸多可能的执行路径中选择一条等级值最低的作为其执行计划,但如果出现了两条或者两条以上等级值相同的执行路径的情况,那么此时RBO会如何选择呢?

此时RBO会依据目标SQL中所涉及的相关对象在数据字典缓存(Data Dictionary Cache)中的缓存顺序和目标SQL中所涉及的各个对象在目标SQL文本中出现的先后顺序来综合判断。这也就意味着我们还可以通过调整相关对象在数据字典缓存中的缓存顺序,改变目标SQL中所涉及的各个对象在该SQL文本中出现的先后顺序来调整其执行计划。

看一个在使用RBO的情况下对目标SQL的执行计划做调整的实例。创建一个测试表EMP_TEMP:

[root@oracle-db-19c ~]# su - oracle
[oracle@oracle-db-19c ~]$ sqlplus / as sysdba

SQL*Plus: Release 19.0.0.0.0 - Production on Sun Jan 29 18:12:01 2023
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.


Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

SQL> conn scott/tiger@PDB1
Connected.
SQL> show user;
USER is "SCOTT"
SQL> set pagesize 200
SQL> set linesize 200
SQL> create table emp_temp as select * from emp;

Table created.

SQL> create index idx_mgr_temp on emp_temp(mgr);

Index created.

SQL> create index idx_deptno_temp on emp_temp(deptno);

Index created.

SQL> 

范例1:


SQL> 
SQL> show user;
USER is "SCOTT"
SQL> select * from emp_temp where mgr > 100 and deptno > 100;

no rows selected

SQL> 

 对于范例SQL 1而言,其where条件中出现了列MGR和DEPTNO,而在列MGR和DEPTNO上分别存在着索引IDX_MGR_TEMP和IDX_DEPTNO_TEMP。

12.如果在启用RBO的情形下执行范例SQL 1,则Oracle会选择走上述两个索引中的哪一个?

实际验证一下。在当前Session中将优化器模式修改为RULE,表示在当前Session中启用RBO

SQL> alter session set optimizer_mode='RULE';

Session altered.

SQL> set autotrace traceonly explain
SQL> select * from emp_temp where mgr > 100 and deptno > 100;

Execution Plan
----------------------------------------------------------
Plan hash value: 1670750536

-------------------------------------------------------
| Id  | Operation                   | Name            |
-------------------------------------------------------
|   0 | SELECT STATEMENT            |                 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP_TEMP        |
|*  2 |   INDEX RANGE SCAN          | IDX_DEPTNO_TEMP |
-------------------------------------------------------

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

   1 - filter("MGR">100)
   2 - access("DEPTNO">100)

Note
-----
   - rule based optimizer used (consider using cbo)

SQL> 

id2的执行步骤说明Oracle在执行上述范例SQL 1时使用的是RBO,且选择的是走对索引IDX_DEPTNO_TEMP的索引范围扫描。

范例SQL 1的where条件中有“mgr>100”,所以RBO实际上可以选择走列MGR上的索引IDX_MGR_TEMP的只不过RBO这里并没有选择走该索引,而是选择走列DEPTNO上的索引IDX_DEPTNO_TEMP

13.假如我们发现走索引IDX_DEPTNO_TEMP不如走索引IDX_MGR_TEMP的执行效率高,或者说我们就想让RBO选择走索引IDX_MGR_TEMP,那么应该如何做呢?

在使用RBO的情况下,可以通过等价改写目标SQL(加0或者空字符串的方式)来调整该SQL的执行计划。列DEPTNO的类型为NUMBER,所以我们可以在列DEPTNO上加0,来达到不让RBO选择走其上的索引IDX_DEPTNO_TEMP的目的。在列DEPTNO上加0后即形成了如下形式的范例SQL 2:

SQL> alter session set optimizer_mode='RULE';

Session altered.

SQL> set autotrace traceonly explain

SQL> select * from emp_temp where mgr > 100 and deptno+0 > 100;

Execution Plan
----------------------------------------------------------
Plan hash value: 2973289657

----------------------------------------------------
| Id  | Operation                   | Name         |
----------------------------------------------------
|   0 | SELECT STATEMENT            |              |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP_TEMP     |
|*  2 |   INDEX RANGE SCAN          | IDX_MGR_TEMP |
----------------------------------------------------

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

   1 - filter("DEPTNO"+0>100)
   2 - access("MGR">100)

Note
-----
   - rule based optimizer used (consider using cbo)

SQL> 

此时Id = 2的执行步骤已经从之前的“INDEX RANGE SCAN | IDX_DEPTNO_TEMP”变为了现在的“INDEX RANGE SCAN |IDX_MGR_TEMP”,这说明我们确实迫使RBO改变了执行计划,即我们的调整已经生效了。 

14. 如果目标SQL出现了有两条或者两条以上的执行路径的等级值相同的情况,该如何调整执行计划?

方法一:

我们可以通过调整相关对象在数据字典缓存中的缓存顺序来影响RBO对于其执行计划的选择对于范例SQL 1而言,对索引IDX_DEPTNO_TEMP走索引范围扫描和对索引IDX_MGR_TEMP走索引范围扫描的等级值显然是相同的,所以我们就可以通过调整这两个索引在数据字典缓存中的缓存顺序来改变执行计划。

刚才我们先创建索引IDX_MGR_TEMP,再创建索引IDX_DEPTNO_TEMP,所以索引IDX_MGR_TEMP 和IDX_DEPTNO_TEMP在数据字典缓存中的缓存顺序是,先缓存IDX_MGR_TEMP,再缓存IDX_DEPTNO_TEMP。这种情形下RBO选择的是走对索引IDX_DEPTNO_TEMP的索引范围扫描,如果我们现在把索引IDX_MGR_TEMP先Drop掉再重新创建一次,那么就相当于是先创建索引IDX_DEPTNO_TEMP,再创建索引IDX_MGR_TEMP,也就是说此时这两个索引在数据字典缓存中的缓存顺序就刚好颠倒过来了。按照此前介绍的知识,此时RBO应该就会选择走对索引IDX_MGR_TEMP的索引范围扫描。

验证实验:

先Drop索引IDX_MGR_TEMP,再重建。

SQL> 
SQL> drop index idx_mgr_temp;

Index dropped.

SQL> create index idx_mgr_temp on emp_temp(mgr);

Index created.

SQL> alter session set optimizer_mode='RULE';

Session altered.

SQL> set autotrace traceonly explain
SQL> select * from emp_temp where mgr > 100 and deptno > 100;

Execution Plan
----------------------------------------------------------
Plan hash value: 2973289657

----------------------------------------------------
| Id  | Operation                   | Name         |
----------------------------------------------------
|   0 | SELECT STATEMENT            |              |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP_TEMP     |
|*  2 |   INDEX RANGE SCAN          | IDX_MGR_TEMP |
----------------------------------------------------

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

   1 - filter("DEPTNO">100)
   2 - access("MGR">100)

Note
-----
   - rule based optimizer used (consider using cbo)

SQL> 

 id= 2的执行步骤已经从之前的“INDEX RANGE SCAN | IDX_DEPTNO_TEMP”变为了现在的“INDEX RANGE SCAN |IDX_MGR_TEMP”,说明我们确实迫使RBO改变了执行计划,这也说明当目标SQL有两条或者两条以上的执行路径的等级值相同时,我们确实可以通过调整相关对象在数据字典缓存中的缓存顺序来影响RBO对于其执行计划的选择。

方法二:

如果目标SQL出现了有两条或者两条以上的执行路径的等级值相同的情况,可以通过改变目标SQL中所涉及的各个对象在该SQL文本中出现的先后顺序来调整该目标SQL的执行计划。通常适用于目标SQL中出现了多表连接的情形在目标SQL出现了有两条或者两条以上的执行路径的等级值相同的前提条件下,RBO会按照从右到左的顺序来决定谁是驱动表,谁是被驱动表,进而会据此来选择执行计划,所以如果我们改变了目标SQL中所涉及的各个对象在该SQL文本中出现的先后顺序,也就改变了表连接的驱动表和被驱动表,进而就调整了该SQL的执行计划。

范例3 SQL 3:

select t1.mgr, t2.deptno
  from emp_temp t1, emp_temp1 t2
 where t1.empno = t2.empno;

对于范例SQL 3而言,表EMP_TEMP和EMP_TEMP1唯一的表连接条件为“t1.empno = t2.empno”,而在表EMP_TEMP和EMP_TEMP1的字段EMPNO上均没有任何索引,按照前面介绍的知识,表EMP_TEMP1 在SQL文本中的位置是在表EMP_TEMP的右边,所以此时RBO会将表EMP_TEMP1作为表连接的驱动表,而将表EMP_TEMP作为表连接的被驱动表。 

SQL> create table emp_temp1 as select * from emp;

Table created.

SQL>

SQL> alter session set optimizer_mode='RULE';

Session altered.

SQL> set autotrace traceonly explain
SQL> select t1.mgr, t2.deptno
  2    from emp_temp t1, emp_temp1 t2
  3   where t1.empno = t2.empno;

Execution Plan
----------------------------------------------------------
Plan hash value: 1323777565

-----------------------------------------
| Id  | Operation           | Name      |
-----------------------------------------
|   0 | SELECT STATEMENT    |           |
|   1 |  MERGE JOIN         |           |
|   2 |   SORT JOIN         |           |
|   3 |    TABLE ACCESS FULL| EMP_TEMP1 |
|*  4 |   SORT JOIN         |           |
|   5 |    TABLE ACCESS FULL| EMP_TEMP  |
-----------------------------------------

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

   4 - access("T1"."EMPNO"="T2"."EMPNO")
       filter("T1"."EMPNO"="T2"."EMPNO")

Note
-----
   - rule based optimizer used (consider using cbo)

SQL> 

从上面显示的内容可以看出,现在范例SQL 3的执行计划走的是排序合并连接,且驱动表确实是表EMP_TEMP1。

注意,从严格意义上来说,排序合并连接并没有驱动表和被驱动表的概念,这里只是为了方便阐述而人为地给排序合并连接添加了上述概念。

将范例SQL 3中的表EMP_TEMP和EMP_TEMP1在该SQL的SQL文本中的位置换一下,即形成了如下形式的范例SQL 4:

select t1.mgr, t2.deptno
  from emp_temp1 t2, emp_temp t1
 where t1.empno = t2.empno;
SQL> 
SQL> select t1.mgr, t2.deptno
  2    from emp_temp1 t2, emp_temp t1
  3   where t1.empno = t2.empno;

Execution Plan
----------------------------------------------------------
Plan hash value: 2135683657

-----------------------------------------
| Id  | Operation           | Name      |
-----------------------------------------
|   0 | SELECT STATEMENT    |           |
|   1 |  MERGE JOIN         |           |
|   2 |   SORT JOIN         |           |
|   3 |    TABLE ACCESS FULL| EMP_TEMP  |
|*  4 |   SORT JOIN         |           |
|   5 |    TABLE ACCESS FULL| EMP_TEMP1 |
-----------------------------------------

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

   4 - access("T1"."EMPNO"="T2"."EMPNO")
       filter("T1"."EMPNO"="T2"."EMPNO")

Note
-----
   - rule based optimizer used (consider using cbo)

SQL> 

 在范例SQL 4的执行计划走的也是排序合并连接且驱动表确实已经由之前的表EMP_TEMP1变为了现在的表EMP_TEMP。这说明我们确实使RBO改变了执行计划,也说明当目标SQL有两条或者两条以上的执行路径的等级值相同时,我们确实可以通过改变目标SQL中所涉及的各个对象在该SQL文本中出现的先后顺序来影响RBO对于其执行计划的选择

注意,这种位置的先后顺序对于目标SQL执行计划的影响是有前提条件的,那就是仅凭各条执行路径等级值的大小RBO难以选择执行计划,也就是说该目标SQL一定有两条或者两条以上执行路径的等级值相同。换句话说,如果RBO仅凭各条执行路径等级值的大小就可以选择目标SQL的执行计划,那么无论怎么调整相关对象在该SQL的SQL文本中的位置,对于该SQL最终的执行计划都不会有任何影响。

验证一下上述结论。看看如下的范例SQL 5:

select t1.mgr, t2.deptno
  from emp t1, emp_temp t2
 where t1.empno = t2.empno;

对于范例SQL 5而言,表EMP和EMP_TEMP唯一的表连接条件为“t1.empno =t2.empno”。对于表EMP而言,列EMPNO上存在主键索引PK_EMP,而对于表EMP_TEMP而言,列EMPNO上不存在任何索引。所以在使用RBO的情况下,范例SQL 5的执行路径将不再仅限于排序合并连接(RBO不支持哈希连接),也就是说RBO此时有可能可以仅凭各条执行路径等级值的大小就选择出范例SQL 5的执行计划。

SQL> 
SQL> select t1.mgr, t2.deptno
  2    from emp t1, emp_temp t2
  3   where t1.empno = t2.empno;

Execution Plan
----------------------------------------------------------
Plan hash value: 367190759

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

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

   4 - access("T1"."EMPNO"="T2"."EMPNO")

Note
-----
   - rule based optimizer used (consider using cbo)

SQL> 

从上面显示的内容可以看出,现在范例SQL 5的执行计划走的是嵌套循环连接,且驱动表是表EMP_TEMP。

将范例SQL 5中的表EMP和EMP_TEMP在该SQL的SQL文本中的位置换一下,即形成了如下形式的范例SQL 6:

select t1.mgr, t2.deptno
  from emp_temp t2, emp t1
 where t1.empno = t2.empno;
SQL> 
SQL> select t1.mgr, t2.deptno
  2    from emp_temp t2, emp t1
  3   where t1.empno = t2.empno;

Execution Plan
----------------------------------------------------------
Plan hash value: 367190759

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

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

   4 - access("T1"."EMPNO"="T2"."EMPNO")

Note
-----
   - rule based optimizer used (consider using cbo)

SQL>

从上面显示的内容可以看出,现在范例SQL 6的执行计划走的还是嵌套循环连接,且驱动表依然是表EMP_TEMP。这就验证了我们之前提到的观点:如果RBO仅凭目标SQL各条执行路径等级值的大小就可以选择出执行计划,那么无论怎么调整相关对象在该SQL的SQL文本中的位置,对于该SQL最终的执行计划都不会有任何影响。 

RBO最大的问题在于它是靠硬编码在Oracle数据库代码中的一系列固定的规则来决定目标SQL的执行计划的,而并没有考虑目标SQL中所涉及的对象的实际数据量、实际数据分布等情况,这样一旦固定的规则并不适用于该SQL中所涉及的实际对象时,RBO根据固定规则产生的执行计划就很可能不是当前情况下的最优执行计划了。

15.基于成本的优化器(CBO)的工作原理是什么?

为了解决RBO的上述先天缺陷,从Oracle 7开始,Oracle就引入了CBO。之前已经提到过,CBO在选择目标SQL的执行计划时,所用的判断原则为成本,CBO会从目标SQL诸多可能的执行路径中选择一条成本值最小的执行路径来作为其执行计划,各条执行路径的成本值是根据目标SQL语句所涉及的表、索引、列等相关对象的统计信息计算出来的。

统计信息是这样的一组数据:它们存储在Oracle数据库的数据字典里,且从多个维度描述了Oracle数据库里相关对象的实际数据量、实际数据分布等详细信息

 成本指Oracle根据相关对象的统计信息计算出来的一个值,它实际上代表了Oracle根据相关统计信息估算出来的目标SQL的对应执行步骤的I/O、CPU和网络资源的消耗量,这也就意味着Oracle数据库里的成本实际上就是对执行目标SQL所要耗费的I/O、CPU和网络资源的一个估算值。

16.网络资源消耗是指什么?

网络资源消耗适用于那些使用了dblink的分布式目标SQL,CBO在解析该类SQL时知道在实际执行它们时所需要的数据并不全部在本地数据库中(需要去远程数据库中取数据),所以此时的网络资源消耗就会被CBO考虑在内。这里需要注意的是,Oracle会把解析这种分布式目标SQL所需要考虑的网络资源消耗折算成对等的I/O资源消耗,所以实际上你可以认为Oracle数据库里的成本仅仅依赖于执行目标SQL时所需要耗费的I/O和CPU资源

另外需要注意的是,在Oracle未引入系统统计信息之前,CBO所计算的成本值实际上全部是基于I/O来估算的,只有在Oracle引入了系统统计信息之后,CBO所计算的成本值才真正依赖于目标SQL的I/O和CPU消耗。

从上述对CBO的介绍中我们可以看出:CBO会从目标SQL诸多可能的执行路径中选择一条成本值最小的执行路径来作为其执行计划,这也就意味着CBO会认为那些消耗系统I/O和CPU资源最少的执行路径就是当前情况下的最佳选择。注意,这里的“消耗系统I/O和CPU资源”(即成本)的计算方法会随着优化器模式的不同而不同。

Oracle在对一条执行路径计算成本时,并不一定会从头到尾完整计算完,只要Oracle在计算过程中发现算出来的部分成本值已经大于之前保存下来的到目前为止的最小成本值,就会马上中止对当前执行路径成本值的计算,并转而开始计算下一条新的执行路径的成本。这个过程会一直持续下去,直到目标SQL的各个可能的执行路径全部计算完毕或已达到预先定义好的待计算的执行路径数量的阈值。

16.什么是集的势(基数)

Cardinality(基数)是CBO特有的概念,直译过来就是“集的势”,它是指指定集合所包含的记录数,说白了就是指定结果集的行数。这个指定结果集是与目标SQL执行计划的某个具体执行步骤相对应的,也就是说Cardinality实际上表示对目标SQL的某个具体执行步骤的执行结果所包含记录数的估算。当然,如果是针对整个目标SQL,那么此时的Cardinality就表示对该SQL最终执行结果所包含记录数的估算。

Cardinality和成本值的估算是息息相关的,因为Oracle得到指定结果集所需要耗费的I/O资源可以近似看作随着该结果集所包含记录数的递增而递增,所以某个执行步骤所对应的Cardinality的值越大,那么它所对应的成本值往往也就越大,这个执行步骤所在执行路径的总成本值也就会越大

17.可选择率的定义是什么?

可选择率(Selectivity)也是CBO特有的概念,它是指施加指定谓词条件后返回结果集的记录数占未施加任何谓词条件的原始结果集的记录数的比率。

可选择率可以用如下的公式来表示:

 从上述计算可选择率的公式可以看出,可选择率的取值范围显然是0~1,它的值越小,就表明可选择性越好。毫无疑问,可选择率为1时的可选择性是最差的。

可选择率和成本值的估算也是息息相关的,因为可选择率的值越大,就意味着返回结果集的Cardinality的值就越大,所以估算出来的成本值也就会越大

实际上,CBO就是用可选择率来估算对应结果集的Cardinality的,上述关于可选择率的计算公式等价转换后就可以用来估算Cardinality的值。这里我们用“Original Cardinality”来表示未施加任何谓词条件的原始结果集的记录数,用“Computed Cardinality”来表示施加指定谓词条件后返回结果集的记录数,CBO用来估算Cardinality的公式如下:

 虽然看起来可选择率的计算公式很简单,但实际上它的具体计算过程还是很复杂的,每一种具体情况都会有不同的计算公式。其中最简单的情况是对目标列做等值查询时可选择率的计算在目标列上没有直方图且没有NULL值的情况下,用目标列做等值查询的可选择率是用如下公式来计算的:

 来看一下CBO会如何计算列MGR的可选择率和该SQL返回结果集的Cardinality。

SQL> delete from emp where mgr is null;

1 row deleted.

SQL> commit;

Commit complete.

SQL> alter table emp modify (mgr not null);

Table altered.

SQL> create index idx_emp_mgr on emp(mgr);

Index created.

SQL> select count(*) from emp;

  COUNT(*)
----------
        13

SQL> select count(distinct mgr) from emp;

COUNT(DISTINCTMGR)
------------------
                 6

SQL> exec dbms_stats.gather_table_stats(ownname => 'SCOTT',tabname => 'EMP',estimate_percent => 100,cascade => true, method_opt=>'for all columns size 1',no_invalidate => false);

PL/SQL procedure successfully completed.

[oracle@oracle-db-19c ~]$ sqlplus / as sysdba

SQL*Plus: Release 19.0.0.0.0 - Production on Sun Jan 29 20:38:25 2023
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.


Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

SQL> conn scott/tiger@PDB1;
Connected.
SQL> set autotrace traceonly
SQL> set linesize 800
SQL> set pagesize 900
SQL> select * from emp where mgr=7902;


Execution Plan
----------------------------------------------------------
Plan hash value: 351129165

---------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |             |     2 |    76 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| EMP         |     2 |    76 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | IDX_EMP_MGR |     2 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------

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

   2 - access("MGR"=7902)


Statistics
----------------------------------------------------------
         62  recursive calls
          0  db block gets
         94  consistent gets
          0  physical reads
          0  redo size
       1105  bytes sent via SQL*Net to client
        394  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          3  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> 

Oracle在解析目标SQL时就会默认使用CBO。注意到上述执行计划的显示内容中有列Rows和列Cost (%CPU),这说明Oracle在解析范例SQL 7时确实使用的是CBO。这里列Rows记录的就是上述执行计划中的每一个执行步骤所对应的Cardinality的值,列Cost (%CPU) 记录的就是上述执行计划中的每一个执行步骤所对应的成本值

从对范例SQL 7的实际执行过程我们可以得到如下结论。

(1)RBO确实是靠硬编码在Oracle数据库代码中的一系列固定的规则来决定目标SQL的执行计划的,并没有考虑目标SQL中所涉及的对象的实际数据量、实际数据分布等情况。而CBO则恰恰相反,CBO会根据反映目标SQL中相关对象的实际数据量、实际数据分布等情况的统计信息来决定其执行计划,这就意味着CBO选择的执行计划可能会随着目标SQL中所涉及的对象的统计信息的变化而变化。CBO的这种变化是颠覆性的,这意味着只要统计信息相对准确,则用CBO来解析目标SQL会比在同等条件下用RBO来解析得到正确执行计划的概率要高。

(2)Cardinality和Selectivity的值会直接影响CBO对于相关执行步骤成本值的估算,进而影响CBO对于目标SQL执行计划的选择。

18.什么是可传递性?

可传递性(Transitivity)也是CBO特有的概念,它是CBO在图1-1的查询转换中所做的第一件事情,其含义是指CBO可能会对原目标SQL做简单的等价改写,即在原目标SQL中加上根据该SQL现有的谓词条件推算出来的新的谓词条件,这么做的目的是提供更多的执行路径给CBO做选择,进而增加得到更高效执行计划的可能性。这里需要注意的是,利用可传递性对目标SQL做简单的等价改写仅仅适用于CBO,RBO不会做这样的事情。

在Oracle里,可传递性又分为如下这三种情形。

1.简单谓词传递

比如原目标SQL中的谓词条件是“t1.c1=t2.c1 and t1.c1=10”,则CBO可能会在这个谓词条件中额外地加上“t2.c1=10”,即CBO可能会将原谓词条件“t1.c1=t2.c1 and t1.c1=10”修改为“t1.c1=t2.c1 and t1.c1=10 and t2.c1=10”。改写前后的谓词条件显然是等价的,因为如果t1.c1=t2.c1且t1.c1=10,那么我们就可以推算出t2.c1也等于10。

2.连接谓词传递

比如原目标SQL中的谓词条件是“t1.c1=t2.c1 and t2.c1=t3.c1”,则CBO可能会在这个谓词条件中额外地加上“t1.c1=t3.c1”,即CBO可能会将原谓词条件“t1.c1=t2.c1 and t2.c1=t3.c1”修改为“t1.c1=t2.c1 and t2.c1=t3.c1 and t1.c1=t3.c1”,同理,这里改写前后的谓词条件也是等价的

3.外连接谓词传递

比如原目标SQL中的谓词条件是“t1.c1=t2.c1(+) and t1.c1=10”,则CBO可能会在这个谓词条件中额外加上“t2.c1(+)=10”,即CBO可能会将原谓词条件“t1.c1=t2.c1(+) and t1.c1=10”修改为“t1.c1=t2.c1(+) and t1.c1=10 and t2.c1(+)=10”。

Oracle利用可传递性对目标SQL做简单的等价改写的目的是为了提供更多的执行路径给CBO做选择,进而增加得到更高效执行计划的可能性。

19.CBO的局限性有哪些?

1.CBO会默认目标SQL语句where条件中出现的各个列之间是独立的,没有关联关系

2.CBO会假设所有的目标SQL都是单独执行的,并且互不干扰

3.CBO对直方图统计信息有诸多限制

CBO对直方图统计信息的限制体现在如下两个方面。

  • 在Oracle 12c之前,Frequency类型的直方图所对应的Bucket的数量不能超过254,这样如果目标列的distinct值的数量超过254,Oracle就会使用Height Balanced类型的直方图。对于Height Balanced类型的直方图而言,因为Oracle不会记录所有的nonpopular value的值,所以在此情况下CBO选错执行计划的概率会比对应的直方图统计信息是Frequency类型的情形要高。
  • 在Oracle数据库里,如果针对文本型的字段收集直方图统计信息,则Oracle只会将该文本型字段的文本值的头32字节给取出来(实际上只取头15字节)并将其转换成一个浮点数,然后将该浮点数作为上述文本型字段的直方图统计信息存储在数据字典里。这种处理机制的先天缺陷就在于,对于那些超过32字节的文本型字段,只要对应记录的文本值的头32字节相同,Oracle在收集直方图统计信息的时候就会认为这些记录该字段的文本值是相同的,即使实际上它们并不相同。这种先天性的缺陷会直接影响CBO对相关文本型字段的可选择率及返回结果集的Cardinality的估算,进而就可能导致CBO选错执行计划。

4.CBO在解析多表关联的目标SQL时,可能会漏选正确的执行计划

20.优化器的模式有哪些?

优化器的模式用于决定在Oracle中解析目标SQL时所用优化器的类型,以及决定当使用CBO时计算成本值的侧重点。这里的“侧重点”是指当使用CBO来计算目标SQL各条执行路径的成本值时,计算成本值的方法会随着优化器模式的不同而不同。

优化器的模式是由参数OPTIMIZER_MODE的值来决定的,OPTIMIZER_MODE的值可能是RULE、CHOOSE、FIRST_ROWS_n(n = 1, 10,100, 1000)、FIRST_ROWS或ALL_ROWS。

OPTIMIZER_MODE的各个可能的值的含义为如下所示。

1.RULE

RULE表示Oracle将使用RBO来解析目标SQL,此时目标SQL中所涉及的各个对象的统计信息对于RBO来说将没有任何作用。

2.CHOOSE

CHOOSE是Oracle 9i中OPTIMIZER_MODE的默认值,它表示Oracle在解析目标SQL时到底是使用RBO还是使用CBO取决于该SQL中所涉及的表对象是否有统计信息。具体来说就是:只要该SQL中所涉及的表对象中有一个有统计信息,那么Oracle在解析该SQL时就会使用CBO;如果该SQL中所涉及的所有表对象均没有统计信息,那么此时Oracle就会使用RBO。

3.FIRST_ROWS_n(n = 1, 10, 100, 1000)

这里FIRST_ROWS_n(n = 1, 10, 100, 1000)可以是FIRST_ROWS_1、FIRST_ROWS_10、FIRST_ROWS_100 和FIRST_ROWS_1000中的任意一个值,其含义是指当OPTIMIZER_MODE的值为FIRST_ROWS_n(n = 1, 10, 100,1000)时,Oracle会使用CBO来解析目标SQL,且此时CBO在计算该SQL的各条执行路径的成本值时的侧重点在于以最快的响应速度返回头n(n = 1, 10, 100,1000)条记录。

4.FIRST_ROWS

FIRST_ROWS是一个在Oracle 9i中就已经过时的参数,它表示Oracle在解析目标SQL时会联合使用CBO 和RBO。这里联合使用CBO和RBO的含义是指在大多数情况下,FIRST_ROWS还是会使用CBO来解析目标SQL,且此时CBO在计算该SQL的各条执行路径的成本值时的侧重点在于以最快的响应速度返回头几条记录(类似于FIRST_ROWS_n);但是,当出现了一些特定情况时,FIRST_ROWS转而会使用RBO中的一些内置的规则来选取执行计划而不再考虑成本。比如当OPTIMIZER_MODE的值为FIRST_ROWS时有一个内置的规则,就是如果Oracle发现能用相关的索引来避免排序,则Oracle就会选择该索引所对应的执行路径而不再考虑成本,这显然是不合理的。与之相对应的,在OPTIMIZER_MODE的值为FIRST_ROWS的情形下,你会发现索引全扫描出现的概率会比之前有所增加,这是因为走索引全扫描能够避免排序的缘故。

5.ALL_ROWS

ALL_ROWS是Oracle 10g以及后续Oracle数据库版本中OPTIMIZER_MODE的默认值,它表示Oracle会使用CBO来解析目标SQL,且此时CBO在计算该SQL的各条执行路径的成本值时的侧重点在于最佳的吞吐量(即最小的系统I/O和CPU资源的消耗量)。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/184228.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

源表的基础知识

浅谈“源表”的定义、功能及应用 源表与传统电源的区别 源表速度更快; 源表电流、电压分辨率更高; 源表是四象限,可以正电压(源表提供给外部)负电流(外部灌入源表)或者负电压、正电流。 传统电…

3.组件的基本用法

目录 1 创建组件 1.1 用函数创建组件 1.2 使用类创建组件 2 项目上组件的使用方式 3 事件处理 3.1 事件绑定 3.1.1 类组件绑定事件 3.1.2 函数组件绑定事件 3.2 事件对象 4 组件状态 4.1 初始化状态 4.2 获取状态 4.3 设置状态 4.3.1 箭头函数 4.…

win10下Elasticsearch安装配置完整教程

一、在安装Elasticsearch引擎之前,必须安装ES需要的软件环境,安装Java JDK和配置JAVA_HOME环境变量 二.安装ElasticSearch服务,下载和安装es包 https://www.elastic.co/downloads/past-releases Elasticsearch 8.6.1 | Elastic 安装成功之…

数据结构 第五章 数组和广义表

还是会想你:点击收听 1 基本知识点 1、数组可以看作是下标和值的偶对的集合(具有相同类型的数据元素) 注意:数组是同类型值的集合?**错误**2、数组的存储方式:以行为主序(一行存储完成之后继续存储下一行)、以列为主序(一列存储…

【C++修行之路】C++入门之深剖变量

🍿本文主题:C语法中的变量 🎈更多内容:C较C的改进 💕我的主页:蓝色学者 文章目录前言概念什么是变量变量名变量类型为什么要有不同数据类型各自数据类型的本质结语前言 大家好久不见,今天是我…

22.1.29打卡 2023牛客寒假算法基础集训营1 ACDHKLM

2023牛客寒假算法基础集训营1_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ 网上高质量题解太多了, 我这就作为打卡日报随便写写 A 四个变量 a b记录两队得分 c d记录两队最多还能获得的得分 简单模拟 /* ⣿⣿⣿⣿⣿⣿⡷⣯⢿⣿⣷⣻⢯⣿⡽⣻⢿⣿⣿⣿⣿⣿⣿⣿⣿…

Ruoyi-Cloud框架学习-【02 Redis安装搭建+Ruoyi下载+Nacos数据库配置】

Redis服务端与客户端的安装与搭建 参考该博文 https://joycez.blog.csdn.net/article/details/128776284?spm1001.2014.3001.5502 安装文件 链接:https://pan.baidu.com/s/1v19C9Dhh-ZkNQ8l1RVGQkA 提取码:6mim Ruoyi-Cloud下载以及数据库配置 1、前…

MySQL主从复制的数据同步延迟(二)

1、介绍 主从复制环境在单机应用的时候没有问题,但是在实际的生产环境中,会存在 复制延迟 的问题 查看从库同步状态 在从库中执行 show slave status\G : mysql> show slave status\G *************************** 1. row **************…

Matlab论文插图绘制模板第73期—带等高线的光影曲面图

在之前的文章中,分享了Matlab曲面图的绘制模板: 光影曲面图: 带等高线的曲面图: 进一步,再来看一下带等高线的光影曲面图的绘制模板。 先来看一下成品效果: 特别提示:Matlab论文插图绘制模板系…

make <board_name>_defconfigmake menuconfig

1.make <board_name>_defconfig (1)在uboot的顶层目录的Makefile中查找目标&#xff08;defconfig无法查到&#xff09;&#xff0c;检索config&#xff0c;找到目标config和目标%config 因为我们的目标为<board_name>_defconfig&#xff0c;所以需要看的是有通配…

发现了一个提取时间序列技术指标的神器

互联网上有一个最简单而又强大的技术库&#xff0c;叫做pandas_ta。这个python库为你提供了一个简化的API&#xff0c;让你从时间序列中提取技术分析指标。今天我将和大家一起学习这个库。记得点赞、收藏&#xff5e; 虽然你可能会将这些函数应用于股票价格&#xff0c;但你可…

python开发环境搭建实践

这里写自定义目录标题前言搭建过程anaconda下载与安装下载安装配置环境变量pycharm下载和安装下载安装激活总结前言 新年新气象&#xff0c;我也把电脑进行了重装系统。所以现在需要重新搭建各种开发环境&#xff0c;也不能说是各种&#xff0c;因为我只会python和Java。 先搭…

全国产交换机基本介绍(分类、性能指标、硬件构成及工作方式)

关于全国产交换机基础知识&#xff0c;您了解多少呢&#xff1f;来和海翎光电的小编一起学习一下吧&#xff01; 一、交换机的分类与性能指标 1.1 交换机的分类 按照网络构成方式来分的话&#xff0c;交换机分为三类&#xff1a;接入层交换机、汇聚层交换机和核心层交换机。按照…

93.transformer、多头注意力以及代码实现

1. Transformer架构 2. 多头注意力 3. 数学上来解释多头注意力 4. 有掩码的多头注意力 5. 基于位置的前馈网络 6. 层归一化 batch norm&#xff1a;比如说一行是一个样本&#xff0c;那么BN就是对一列进行归一化&#xff0c;就是对所有数据项的某一列特征进行归一化 layer nor…

【数据结构和算法】了解认识栈,并实现栈的相关函数

到现在我们了解并认识了线性表的概念&#xff0c;动态、静态顺序表的建立&#xff0c;以及两种链表的实现&#xff0c;接下来我们要认识一个新的内容&#xff0c;新的概念&#xff0c;栈&#xff0c;是基于顺序表或者链表的一种新型数据结构。 目录 一、栈是什么&#xff1f; …

RabbitMQ入门中篇

本篇博文目录一.Spring整合RabbitMQ1.导入依赖2.生产者3.消费者4.测试二.SpringBoot整合RabbitMQ1.导入依赖2.生产者3.消费者4.测试三.代码下载一.Spring整合RabbitMQ 在spring项目中使用RabbitMQ的Exchange模式的Topics&#xff0c;项目分为消费者spring项目和生产者spring项目…

Centos7安装kvm服务器

Centos7安装kvm服务器什么是kvm检查硬件是否支持kvm虚拟化启用嵌套虚拟化(可选)查看是否启用嵌套虚拟化嵌套虚拟化立即生效(临时)重新加载模块并验证虚拟机硬件直通虚拟机内查看嵌套虚拟化是否成功安装kvm服务器安装kvm的所有包(不推荐)启动libvirt服务查看是否正确设置了虚拟化…

Linux操作系统之线程安全

一、引入线程安全关于strtok函数不能在多线程中使用&#xff0c;为什么&#xff1f;运行结果原因如下&#xff1a;在strtok内部会有一个全局变量静态指针&#xff08;static char* ptr&#xff09;&#xff0c;一开始指针指向主线程的a&#xff0c;等到后面子线程开始分割时&am…

18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)

Java反射18 反射reflect18.1 类的加载18.2 Class对象18.3 获取类对象的3种方法18.4 反射通用操作18.4.1 常见方法18.4.2 通用操作18.5 设计模式18.5.1 概念18.5.2 好处18.5.3 工厂设计模式18.5.4 单例模式18.6 枚举18.7 注解18.7.1 概念18.7.2 定义注解18.7.3 注解属性类型18.7…

知名休闲服饰品牌——慕尚集团借力泛微实现统一办公、业务协同

客户简介 宁波中哲慕尚控股有限公司&#xff08;以下简称慕尚集团&#xff09;是中国知名的由新零售模式驱动的休闲时尚服饰多品牌运营公司。 旗下品牌有GXG,gxgjeans,gxg.kids,MODE COMMUTER等&#xff0c;覆盖时尚男女装、童装市场和其他时尚领域。2019年5月慕尚集团正式登…