• oracle 执行计划详解


    执行计划是指示 Oracle 如何获取和过滤数据、产生最终的结果集,是影响SQL 语句执行性能的关键因素。在深入了解执行计划之前,首先需要知道执行计划是在什么时候产生的,以及如何让 SQL 引擎为语句生成执行划。

    先了解 SQL 语句的处理执行过程。当一条语句提交到 Oracle 后,SQL 引擎会分为三个步骤对其处理和执行:解析(Parse)、执行(Execute)和获取(Fetch),分别由 SQL 引擎的不同组件完成。

    在这里插入图片描述

    Oracle SQL 引擎的体系结构

    一、SQL 编译器(SQL Compiler)

    SQL 编译器由解析器(Parser)、查询优化器(Query Optimizer)和行源生成器(Row Source Generator)组成。

     解析器(Parser):执行对 SQL 语句的语法、语义分析,将查询中的视图展开、划分为小的查询块。
     查询优化器(Query Optimizer):Oracle 数据库中 SQL 优化器(SQLOptimizer)是 SQL 分析和执行的优化工具,为语句生成一组可能被使用的执行计划,估算出每个执行计划的代价,并调用计划生成器(Plan Generator)生成计划,比较计划的代价,最终选择一个代价最小的计划,优化器负责生成 SQL 的执行计划。它对优化器来说输入的是解析后的 SQL 语句,输出的是执行计划。
    查询优化器由查询转换器(Query Transform)、代价估算器(Estimator)和计划生成器(Plan Generator)组成。
    在这里插入图片描述

    Oracle 两种优化器
     RBO(Rule-Based Optimizer):基于规则的优化器
     CBO(Cost-Based Optimizer):基于成本的优化器(11g 默认优化器)

    从 Oracle 10g 开始,RBO 已经被弃用,但是我们依然可以通过 Hint 方式来使用它。
    上述优化器实际上指的是基于代价的优化器(Cost Based Optimizer,CBO),CBO 也是当前采用的所有优化和调优技术的核心基础。

    查询转换器(Query Transformer):决定是否重写用户的查询(包括视图合并、子查询反嵌套),以生成更好的查询计划。

    代 价 估 算 器 ( Estimator ) : 使 用 统 计 数 据 来 估 算 操 作 的 选 择 率(Selectivity)、返回数据集(Cardinality)和代价,并最终估算出整个执行计划的代价。

    计划生成器(Plan Generator):计划生成器会考虑可能的访问路径(AccessPath)、关联方法和关联顺序,生成不同的执行计划,让查询优化器从这些计划中选择出代价最小的一个计划。

    行源生成器(Row Source Generator):从优化器接收到优化的执行计划后,为该计划生成行源(Row Source)。行源是一个可被迭代控制的结构体,它能以迭代方式处理一组数据行、并生成一组数据行。

    SQL 执行引擎(SQL Execution Engine):SQL 执行引擎依照语句的执行计划进行操作,产生查询结果。在每一个操作中,SQL 执行引擎会以迭代方式执行行源、生成数据行。

    Oracle 引入一些新的优化技术时,例如,SPM、SPA 等,这些组件会与 SQL引擎的组件融合,提供更好的优化和调优方法。

    CBO 优化模式
    (1)FIRST_ROWS(n)
    (2)ALL_ROWS (默认值)

    查看参数:

    SQL> show parameter optimizer_mode
    
    • 1

    在同一系统环境下,同一条 SQL 语句选用不同的优化模式,将可能令优化器生成不同的执行计划。

    修改 CBO 优化 模式:
    (1)Sessions 级别:

    SQL> alter session set optimizer_mode = all_rows;
    
    • 1

    (2)系统级别:修改 spfile 参数

    SQL> alter system set OPTIMIZER_MODE = ALL_ROWS;
    
    • 1

    (3)语句级别:用 Hint(/*+ … */)来设定

    SQL> Select /*+ first_rows(10) */ name from table;
    
    • 1

    二、SQL 语句的执行过程

    用户发出待执行的 SQL 语句 -> 解析 -> 优化器(查询转换,RBO 或 CBO处理)-> 生成执行计划 -> 实际执行 -> 返回结果。
    在这里插入图片描述
    处理步骤包括:
    1、语法检查(syntax check): 检查此 sql 的拼写是否语法。例如:

    SELECT * FORM employees;
    
    • 1

    2、语义检查(semantic check): 访问对象是否存在及该用户是否具备相应的权限。例如:

    SELECT * FROM nonexistent_table;
    
    • 1

    3、共享池检查(Shared Pool Check),生成 SQL 语句的 Hash 值和 SQL_ID,按 SQL_ID 及其 Hash 值 Shared Pool 中查找匹配的相同 SQL 语句。

    4、加载 SQL 代码至内存,生成内存共享数据(cursor),生成执行计划(execution plan)。

    5、执行 SQL,返回结果(execute and return)。

    SQL 解析类型:
    Hard Parse (硬解析): 也称为"library cache miss"。进行语法检查、语义检查,加载 SQL 语句至 Shared pool 的 Library Cache 中,生成执行计划。

    大小写、空格等差异都会令同一条语句被解析为不同的语句。由于须要加载到内存中,需要专门分配内存空间并进行内存管理,因此,硬解析需要占用 CPU、获取 library cache latch 和 shared pool latch 等资源,对于 SQL 的执行来说,硬解析是最消耗资源的。所以,应当尽量避免解析,力求实现 SQL 语句的一次解析,多次执行。

    Soft Parse (软解析): 也称为"library cache hit"。在 SQL 语法和语义检查后,如果在 Shared Pool 中找到了与之完全相同的 SQL 语句,则无需执行内存加载,直接调用已有的执行计划并执行。

    提高软解析的方法包括:增加 shared_pool_size,使用绑定变量优化 SQL语句,调整 cursor_sharing 参数等。

    Softer Soft Parse(软软解析): 当设置了 session_cached_cursors 这个参数之后,Cursor 被直接 Cache 在当前 Session 的 UGA(User Global Area)中的,会话端重复执行相同的 SQL 时,先在 UGA 中查找,如果发现完全相同的Cursor,就直接到 Shared pool 中取结果,也就实现了 Softer Soft Parse。

    SQL>alter system flush shared_pool;
    SQL>create table test as select * from dba_objects where 1<>1;
    --收集 test 表统计信息
    SQL>exec dbms_stats.gather_table_stats('sys','test');
    
    • 1
    • 2
    • 3
    • 4

    下面语句为硬解析:

    SQL>select * from test where object_id=20;
    SQL>select * from test where object_id=30;
    SQL>select * from test where object_id=40;
    SQL>select * from test where object_id=50;
    
    • 1
    • 2
    • 3
    • 4

    下面语句为软件解析:

    var oid number
    exec :oid:=20;
    select * from test where object_id=:oid;
    exec :oid:=30;
    select * from test where object_id=:oid;
    exec :oid:=40;
    select * from test where object_id=:oid;
    exec :oid:=50;
    select * from test where object_id=:oid;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    下面语句为软软解析:

    declare
    i number;
    begin
    for i in 1..14 loop
    execute immediate 'select * from test where object_id=:i' using i;
    end loop;
    end;
    /
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    查询语句执行情况:

    col sql_text format a40
    select sql_text,parse_calls,loads,executions from v$sql s where sql_text like 'select * from test where object_id%' order by 1,2,3,4;
    SQL_TEXT                              PARSE_CALLS   LOADS   EXECUTIONS
    ------------------------------------- ----------- ---------- ----------
    select * from test where object_id=20 1            1              1
    select * from test where object_id=30 1            1              1
    select * from test where object_id=40 1            1              1
    select * from test where object_id=50 1            1              1
    select * from test where object_id=:i 1            1              14
    select * from test where object_id=:oid 4         1             4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    设置 cursor_sharing 参数为 force,设置为 force 后,oracle 将 2 条类似的SQL 的谓词用一个变量代替,同时将它们看做同一条 SQL 语句处理。这种方式很粗暴,但极可能造成执行计划的不准确。适用场景:在无法将应用的代码修改为绑定变量情况下,oracle 提供的一种解决方法。

    alter system flush shared_pool;
    alter session set cursor_sharing=force;
    
    • 1
    • 2

    查询语句:

    select * from test where object_id=60;
    select * from test where object_id=70;
    select * from test where object_id=80;
    select * from test where object_id=90;
    select * from test where object_id=100;
    select * from test where object_id=110;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    查询语句执行情况:

    col sql_text format a50
    select sql_id,child_number,sql_text,buffer_gets
    from v$sql where sql_text like 'select * from test where object_id%';
    
    • 1
    • 2
    • 3

    三、生成和显示执行计划

    任何一条 SQL 语句要正确运行并返回结果,SQL 执行引擎都必须获得一个相应的执行计划。当缓存当中找不到与当前环境相匹配的执行计划时,SQL 编译器会解析和生成一个相应的执行计划。已经生成的执行计划会驻留在缓存当中,直至其失效或者被清出缓存。

    执行计划 (Execution Plan)

     执行计划显示一条 SQL 语句的详细执行步骤,包括从数据库读取数据以及对数据的处理。
     这些步骤表示为一组数据库运算操作(Operation),使用这些运算操作并返回数据行。
     这些运算操作及其实施的顺序由优化器使用查询转换及物理优化技术的组合来确定。
     执行计划通常以表格的形式显示,但它实际上为树形。
    在这里插入图片描述
    同样的 SQL 语句,其执行计划会因用户环境(Schemas)和执行成本(Costs)的不同而改变,例如:
     在不同的数据库中执行;
     以不同的用户执行;
     用户数据方案变化(例如修改了索引);
     不同的数据量和统计信息;
     语句的变量参数输入了不同的类型和取值;
     系统初始参数的变化(包括改变了全局或局部会话的参数等,如:优化模式改变等)。

    通过包 dbms_xplan 显示计划

    dbms_xplan 包可以根据我们选择的函数以及输入的参数来格式化显示相关的执行计划。dbms_xplan 使用函数用于输出格式化的执行计划,display、display_cursor、display_sqlset,分别用于显示 explain plan 命令解释的计划、内存中的执行计划、sql 优化集中语句的计划。

    display函数
    display 函数用于显示存储在 plan_table 中的执行计划,此外,如果从视图v$sql_plan_statistics_all 可以获得该执行计划的相关统计数据,display 也可以格式化输出这些数据。

    参数描述:
    table_name:存储查询计划的表名(不区分大小写),默认值为 plan_table。

    statement_id:sql 语句 id。在 plan_table 中,每条语句的执行计划都会有一个唯一的 id 来标识。这个 id 可以在执行 explain plan 命令时,通过 set statement_id 子句来指定。如果输入为 null,则会获取最近一条被解释的语句。

    format:输出格式。在 display 函数中,有以下预定义的格式(模板)可供选择:

    • ‘basic’:基本格式。输出的内容最少,仅仅输出查询计划中每个操作的 id、名称和选项以及操作的对象名。
    • ‘typical’:典型格式。输出的内容是我们进行语句调优时大多数情况下所需要的信息。除了基本格式中的内容外,还会输出优化器估算出的每个操作的记录行数、字节数、代价和时间,以及相关的提示信息(如远程 sql、优化器建议等)。如果存在谓词(predicate)条件,还会输出每个操作中的过滤(filter)条件和访问(access)条件。此外,如果查询涉及分区表,还会输出分区裁剪信息;如果查询涉及并行查询,还会输出并行操作的相关信息(如表队列信息、并行查询分布方式等)。这种格式是默认格式。
    • ‘serial’:串行执行格式。这种格式和典型格式的输出内容基本一致,不同
      之处在于,对并行查询,它不会输出相关的并行内容。
    • ‘all’:完全格式。输出的内容相对完整。除了典型格式的内容以外,还会输出字段投射信息和别名信息。

    除了这些预定义的格式外,用户还可以通过在格式化字符串中添加或者屏蔽一些关键词进行细化输出。每一个细化选项代表了输出内容中的单个信息(可能是执行计划表中的一个列,也可能是一个附加信息)。在 display 函数中,有以下细化控制选项可供选择:

    • rows:优化器估算出的记录行数;
    • bytes:优化器估算出的字节数;
    • cost:优化器估算出的代价;
    • partition:分区裁剪;
    • parallel:并行查询;
    • predicate:谓词;
    • projection:字段投射;
    • alias:别名;
    • remote:分布式查询信息;
    • note:相关注释信息。

    细化控制选项和预定格式一起使用。例如,如果希望输出基本格式内容,并输出优化器估算出的记录行数,可以用“basic rows”作为格式字符串;而如果希望输出典型格式,但不要其中的谓词条件,则可以输入“typical -predicate”作为格式字符串,即在希望被屏蔽信息的对应控制选项前加上“-”。

    filter_preds:该参数接收合法的谓词过滤条件(可以是谓词逻辑表达式,也可以包含子查询),以过滤从查询计划表中读取的内容。例如,可以输入“cost >10”以限制输出所有估算代价大于 10 的操作。

    --生成执行计划
    explain plan for select * from scott.dept;
    
    --典型格式输出执行计划,即默认 typical 格式输出
    select * from table(dbms_xplan.display());
    ---------------------------------------------
    |Id| Operation        | Name | Rows | Bytes | Cost (%CPU)| Time     |
    --------------------------------------------------------------------
    |0 | SELECT STATEMENT |      | 4    | 80    | 3 (0)      | 00:00:01 |
    |1 | TABLE ACCESS FULL| DEPT | 4    | 80    | 3 (0)      | 00:00:01 |
    --------------------------------------------------------------------
    
    --基本格式输出执行计划,包括 ROWS BYTES
    select * from table(dbms_xplan.display(null,null,'BASIC ROWS BYTES'));
    -----------------------------------------------
    | Id | Operation        | Name | Rows | Bytes |
    -----------------------------------------------
    | 0  | SELECT STATEMENT |      | 4    | 80    |
    | 1  | TABLE ACCESS FULL| DEPT | 4    | 80    |
    -----------------------------------------------
    
    --基本格式输出执行计划,包括 ROWS BYTES COST
    select * from table(dbms_xplan.display(null,null,'BASIC ROWS BYTES COST'));
    ---------------------------------------------------------------
    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
    ---------------------------------------------------------------
    | 0 | SELECT STATEMENT | | 4 | 80 | 3 (0)|
    | 1 | TABLE ACCESS FULL| DEPT | 4 | 80 | 3 (0)|
    ---------------------------------------------------------------
    
    --典型格式输出执行计划
    select * from table(dbms_xplan.display(null,null,'TYPICAL'));
    -----------------------------------------------------------------
    |Id|Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
    -----------------------------------------------------------------
    |0| SELECT STATEMENT | | 4 | 80 | 3 (0)| 00:00:01 |
    |1| TABLE ACCESS FULL| DEPT | 4 | 80 | 3 (0)| 00:00:01 |
    -----------------------------------------------------------------
    
    --屏蔽 rows
    select * from table(dbms_xplan.display(null,null,'TYPICAL -rows'));
    -----------------------------------------------------------------
    | Id | Operation | Name | Bytes | Cost (%CPU)| Time |
    -----------------------------------------------------------------
    | 0 | SELECT STATEMENT | | 80 | 3 (0)| 00:00:01 |
    | 1 | TABLE ACCESS FULL| DEPT | 80 | 3 (0)| 00:00:01 |
    -----------------------------------------------------------------
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    display_cursor函数
    display_cursor 函数可以显示内存中一个或者多个游标的执行计划。同样,可以通过输入参数限定 sql、游标以及输出格式。用户必须对视图 v$sql、v$sql_plan 和 v$sql_plan_statistics_all 的 select 有权限,才能正常调用display_cursor 函数。

    参数描述:
    sql_id:所显示执行计划的 sql 语句的 id。该 id 可以从 v$sql.sql_id、v$session.sql_id 或者v$session.prev_sql_id 获得。如果没有指定 sql_id(指定 null),则默认会显示当前会话中最后一条执行的 sql 语句。

    cursor_child_no:语句的子游标序号。我们知道,受到执行环境的影响,一条 sql 语句可能会产生多个版本的子游标,每个子游标都会与一个执行计划相映射(多个子游标也可能映射同一个执行计划)。通过 cursor_child_no 可以限制仅显示某一个子游标的执行计划。如果不指定该参数,则会显示该语句的所有子游标的执行计划。

    format:格式化控制字符串。display 函数的格式化控制字符串的所有选项都 适用于 display_cursor 函 数 。 由 于 运 行 语 句 还 可 以 通 过 提 示gather_plan_statistics 或设置系统参数 statistics_level 为“all”收集语句运行的性能统计数据,因此在细化选项中还有额外的选项,以选择是否输出这些数据。

    iostats:是否输出计划的输入输出(io)统计数据;

    memstats:在启用了 pga 自动管理(参数 pga_aggregate_target 的值大于 0)的情况下,是否输出计划的输入内存统计数据(操作的内存使用量、内存读次数等);

    allstats:包含了 iostats 和 memstats 的全部内容;

    last:以上三个选项输出的统计数据都是实际产生的数据,而非估算数据,它们是该游标所有执行所产生的数据的总和。你可以增加 last 选项以限定仅显示最后一次运行的统计数据。

    此外,还有一些未公布的选项可用于该函数的输出控制。首先是预定义格式:
    ‘advanced’:高级格式。高级格式除了会输出完全格式中的所有内容外,还会视情况输出绑定变量窥视信息和计划概要(outline)信息;
    outline:是否以提示(hint)的方式显示计划概要;
    peeked_binds:是否显示绑定变量窥视信息;
    buffstats:是否显示内存读次数(包括一致性读和当前读次数),该信息为 iostats 的一部分;
    plan_hash:是否显示计划的哈希值,该选项同样适用于 display 函数。

    -- Show the execution plan in the cursor
    --执行SQL
    select e.last_name, d.department_name from hr.employees e,hr.departments d where e.department_id = d.department_id;
    
    --查看执行计划
    select * from table(dbms_xplan.display_cursor());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    dbms_xplan.display_cursor 的 3 个参数:
    1)sql_id: SQL 语句在游标缓存区的 ID。默认为 NULL,此会话中最后一条执行的语句。
    2)cursor_child_no:子编号,默认为 0。
    3)format: 显示格式。默认为 TYPICAL,可选 basic/typical/serial/all等。

    通过 autotrace 显示计划

    autotrace 是 oracle 自带 的客户 端工 具 sqlplus 的一 个特性 。启用autotrace 后,sqlplus 会自动收集执行过的语句的执行计划、性能统计数据等,并在语句执行结束后显示在 sql*plus 中。

    要使用 autotrace,需要先做以下准备,用 dba 用户创建角色 plustrce,并将该角色赋予用户:

    conn / as sysdba
    @?/sqlplus/admin/plustrce.sql
    grant plustrace to scott;
    
    • 1
    • 2
    • 3

    在执行语句之前,在 sql*plus 中打开 autotrace。可以在打开 autotrace时选择不同选项,以控制输出的内容。选项如下所示:
    set autotrace on:打开 autotrace,并输出所有内容,包括语句本身的查询结果、执行计划,以及性能统计数据。
    set autotrace on explain:打开 autotrace,并输出语句本身的查询结果和执行计划,不输出性能统计数据。
    set autotrace on statistics:打开 autotrace,并输出语句本身的查询结果和性能统计数据,不输出执行计划。
    set autotrace trace:打开 autotrace,并输出执行计划和性能统计数据,不输出语句本身的查询结果。
    set autotrace trace explain:打开 autotrace,并输出执行计划,不输出语句本身的查询结果和性能统计数据。
    set autotrace trace statistics:打开 autotrace,并输出性能统计数据,不输出语句本身的查询结果和执行计划。
    set autotrace off:关闭 autotrace。

    一个完整的 autotrace 报告输出包括三个部分:
    第一部分为 sql 本身的执行结果;
    第二部分为 sql 的执行计划;
    第三部分为 sql 实际执行的性能统计数据。

    由于执行计划和执行的性能数据都是进行 sql 调优时的重要参考信息,因此autotrace 是进行 sql 语句性能调优的一个非常实用的辅助方法。
    当打开 autotrace 后,在执行语句之前,oracle 会调用 explain plan 命令对语句进行解析;在执行完成后,从 plan_table 中查询和显示执行计划。因此,由于受到共享游标、绑定变量窥视等设置的影响,这一执行计划可能会与实际执行计划不同。除了上述方法外,还可以通过其他一些途径获取到语句的执行计划。

    四、解读执行计划

    掌握了获取和显示执行计划的方法后,就可以开始了解如何来读懂一个执行计划。一个执行计划实际上是一个树状关系的结构图。在这个树状结构图中,每一个节点代表了一个操作(相应的,它们在 PLAN_TABLE、V$SQL_PLAN 等执行计划表、视图中都有一条记录),每个支节点(或者说父节点)下都有一个或多个子操作;除根节点外,每个节点都仅隶属于一个支节点。同一层操作按照它们的ID 顺序执行。操作由顶向下调用,即父操作调用子操作;数据由底向上返回,即子节点获取到的数据在处理完成后返回给父节点。

    执行计划包含了相当丰富的信息。通过执行计划,不仅可以知道 SQL 引擎将以什么样的访问顺序访问对象、获取语句的输出结果,而且还可以获得优化器估算出的代价结果、运行时间以及其他一些额外信息。

    exec sql_explain('select o.owner, o.object_name, o.object_id 
    from t_users u, t_objects o 
    where u.username=o.owner and o.object_name like :A and u.user_id=:B','BASIC PREDICATE');
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    以表格样式输出查询计划。该表有多个列,其中 ID 是计划中每个操作的唯一序列号,Operation 是每个操作的名称和方式,Name 是操作的对象。实际上,该表还有其他一些列代表了优化、统计等信息。

    再看每行记录的数据,每行数据中的 Operation 都带有长短不一的前导空格使之看起来成为一个树状结构。这个结构也就是操作之间的调用关系。

    首先看第一条,ID 为 0,操作为 SELECT STATEMENT。这一行实际上只表示这条语句的类型是一条 SELECT 语句,而非一个真正的操作。因此在一些执行计划的显示当中,没有显示 ID 为 0 的操作。

    ID 为 1 的操作是 NESTED LOOPS,表明它需要对两个数据集以嵌套循环的方式进行数据关联。而这两个数据集则是由其两个子操作 2 和 4 分别从表 T_USERS和 T_OBJECTS 上读取得来,也就是说,操作 1 按顺序调用操作 2 和 4,获取它们返回的数据进行关联。而要实现嵌套循环,就需要两个循环体。其中,操作 2就是第一个循环体,也就是外循环;操作 4 就是第二个循环体,即内循环。

    ID 为 2 的操作是 TABLE ACCESS BY INDEX ROWID,Name 是 T_USERS,表明它是通过索引上的 ROWID 来访问表 T_USERS 以获取数据。而索引上的 ROWID 则需要通过其子操作 3 来获取;

    ID 为 3 的操作是 INDEX UNIQUE SCAN,Name 是 T_USERS_PK,表明它是对索引 T_USERS_PK 进行唯一键值的访问以获取其父操作所需要的 ROWID。T_USERS_PK是表 T_USERS 的主键,也是一个唯一索引。而对唯一索引的唯一键值的访问,需要有一个数值的输入作为访问条件。在它的 ID 列,我们可以留意到*符号,表示这个操作有相关的谓词条件(访问条件或者过滤条件)。而我们这里也特地显示了 谓 词 条 件 。 在下面 谓词信息输出部分 ,可以找到一条信息 3 -access(“U”.“USER_ID”=TO_NUMBER(:B)),表明这是操作 ID 为 3 的谓词条件,其中 access 表示它是访问条件,内容是通过某个数值定位 USER_ID 键值。

    访问条件和过滤条件都属于谓词条件,但它们对操作的作用大不相同。访问条件可以帮助操作从物理对象上定位到符合条件的数据,然后再读取数据;而过滤条件是操作已经从物理存储上读取到了数据,然后将不符合条件的数据过滤掉。它们对语句的性能影响很大,了解了它们之间的差别,就有助于我们对语句进行进一步调优。

    ID 为 4 的操作是 TABLE ACCESS FULL,Name 是 T_OBJECTS,表明它是对表T_OBJECTS 进行全表扫描。全表扫描即读取表的物理段(Segment)的高水位线(High Water Mark,HWM)以下的所有数据块。同样,它的 ID 也有*符号,从谓词信息部分可以找到关联的谓词条件 4 - filter(“O”.“OBJECT_NAME” LIKE :A
    AND “U”.“USERNAME”=“O”.“OWNER”)。filter 表明它是一个过滤条件,即读取了表 T_OBJECTS 的所有数据,再过滤掉不符合条件(“O”.“OBJECT_NAME” LIKE :A AND"U".“USERNAME”=“O”.“OWNER”)的数据。

    通过执行计划,可以清楚地了解一条语句是通过什么样的方式读取物理对象的数据,如何对数据进行处理(过滤、排序等),最终获取到符合条件的数据。再结合执行计划中的其他数据,可以进一步定位语句的性能瓶颈在哪里,从而为实施优化奠定基础。

    在执行计划中,除了 ID、Operation 和 Name 之外,还有其他一些列。这些列的数据是根据需要从 PLAN_TABLE、V S Q L P L A N 、 V SQL_PLAN、V SQLPLANVSQL_PLAN_STATISTICS_ALL等表和视图中读取的。它们可以帮助我们进一步理解该执行计划(例如优化器对各个操作的估算数据、实际运行中各个操作的性能数据等)。以下是各个列的描述。

    Rows/E-Rows:优化器估算出当前操作返回给上一级操作的数据记录数,如果计划中同时输出收集到实际记录数,则会显示为 E-Rows 以和实际记录数区别,在优化器中,又称为数据集的行数(Cardinality);

    Bytes/E-Bytes:优化器估算出当前操作返回给上一级操作的数据的字节数,如果计划中同时输出收集到实际字节数,则会显示为 E-Bytes 以和实际字节数区别;

    TempSpc/E-Temp:优化器估算出完成当前操作(仅部分操作需要临时空间,如 SORT、Hash Join)所需要的临时表空间的大小,如果计划中同时输出收集到实际临时空间大小,则会显示为 E-Temp 以和实际临时空间大小区别;

    Cost(%CPU):优化器估算出完成当前操作的代价(包含子操作的代价),它是 IO 代价和 CPU 代价总和。其中,IO 代价是最基本的代价。而对于 CPU 代价,在默认情况下,优化器会将 CPU 代价计算在内,并且将 CPU 代价根据系统配置由特定的转换公式转换为 IO 代价。也可以通过优化器参数_optimizer_cost_model
    指定是否在代价模型中包括 CPU 代价。括号中数据即为 CPU 代价在总代价中的比例;

    Time/E-Time:优化器估算出完成当前操作所需要的时间,这个时间是其子操作的累计时间,如果计划中同时输出收集到实际时间,则会显示为 E-Time 以和实际时间区别;

    Pstart:分区裁剪(Partition Prunning)后,访问的起始分区,仅在含有分区表访问操作的执行计划中出现;

    Pstop:分区裁剪(Partition Prunning)后,访问的结束分区,仅在含有分区表访问操作的执行计划中出现;

    Inst:分布式查询中,远程对象所在的数据库实例名;

    TQ:并行查询中的表队列(Table Queue),我们会在相关操作中进一步阐
    述该列数据;

    IN-OUT:并行查询或分布式查询中数据传输方式;

    PQ Distrib:并行查询中,并行服务进程之间的数据分发方式;

    Starts:当前操作实际被启动的次数,如果输出格式中指定了 LAST 关键字,则为计划最后一次执行中当前操作实际被启动的次数,否则为所有被启动次数总和;

    Rows:当前操作实际返回的记录数,如果输出格式中指定了 LAST 关键字,则为最后一次执行的记录数,否则为所有执行的记录数总和;

    Time:执行当前操作的实际时间,如果输出格式中指定了 LAST 关键字,则为最后一次执行的时间,否则为所有执行的时间总和;

    Buffers:当前操作中发生读内存的次数,如果输出格式中指定了 LAST 关键字,则为最后一次执行的读内存次数,否则为所有执行的读内存次数总和。内存读次数包括一致性读(Consistent Read,CR)和当前模式读(Current Get,CU);

    Reads:当前操作中发生读磁盘的次数,如果输出格式中指定了 LAST 关键字,则为最后一次执行的读磁盘次数,否则为所有执行的读磁盘次数总和;

    Writes:当前操作中发生写磁盘的次数,如果输出格式中指定了 LAST 关键字,则为最后一次执行的写磁盘次数,否则为所有执行的写磁盘次数总和;

    OMem:当前操作完成所有内存工作区(Work Area)操作所总共使用私有内存(PGA)中工作区的大小。需要使用内存工作区的操作为:哈希操作,如哈希分组(Hash Group)、哈希关联(Hash Join)和排序(Sort)操作,它们分别占有工作区中哈希区(Hash Area)和排序区(Sort Area)进行工作,这个数据是由优化器统计数据以及前一次执行的性能数据估算得出的;

    1Mem:当工作区大小无法满足操作所需要的大小时,需要将部分数据写入临时磁盘空间中(如果仅需要写入一次就可以完成操作,就称为一次通过,

    One-Pass;否则为多次通过,Multi-Pass)。该列数据为语句最后一次执行中,单次写磁盘所需要的内存大小,这个数据是由优化器统计数据以及前一次执行的性能数据估算得出的;

    Used-Mem:语句最后一次执行中,当前操作所使用的内存工作区大小,括号里面为(发生磁盘交换的次数,1 次即为 One-Pass,大于 1 次则为 Multi-Pass,如果没有使用磁盘,则显示 OPTIMAL);

    Used-Tmp:语句最后一次执行中,当前操作所使用的临时段的大小,无法一次在工作区完成操作的数据被临时写入该段;

    O/1/M:语句所有的执行总共使用内存或磁盘完成操作的执行次数,分别是Optimal(优化的,仅适用内存完成操作)/One-Pass/Multi-Pass;

    Max-Tmp:语句所有执行中,当前操作所使用的临时段的最大空间。

    通常我们所说的执行计划操作包含两个部分:操作与其选项。例如,哈希关联反关联(HASH JOIN ANTI)中,哈希关联(HASH JOIN)是一种操作,“反”关联(ANTI)则是其选项;该操作还可以与其他选项(如“半”关联,SEMI)配合形成不同的执行计划操作。

    实际上,在执行计划里出现的操作包含两个信息,一个是操作类型(在PLAN_TABLE、V$SQL_PLAN 等表或视图中,字段名为 OPERATION);一个是操作的选项(在相关表和视图中,字段名为 OPTIONS)。例如 TABLE ACCESS BY INDEX ROWID,它的操作类型是 TABLE ACCESS,即访问表,选项是 BY INDEX ROWID,即通过索引中的 ROWID 来访问表。

    执行计划的谓词(Predicate Information)
    是查询语句中的 WHERE 子句中的过滤条件。
     Access: 表示这个谓词条件中的值将会影响数据的访问方式(表、索引、Hash等)。
     Filter:表示这个谓词条件中的值起了数据过滤的作用。

    执行计划的备注(Note)
    补充说明本次执行计划的内容。如:使用了动态采样、专门的查询优化技术、指
    定的 SQL Profile 等。

    执行计划的统计项目
    在这里插入图片描述
    执行计划的内容
    执行计划的执行顺序:按缩行深度及 Id 顺序,一般遵循规则 " 最右最上最先 执行"。即:先从最开头一直往右看,直到看到最右边的并列的地方,对于不并列的,靠右的先执行;对于并列的,靠上的先执行。

    在这里插入图片描述
    Cost ( 执行计划的 开销)
    Cost 是一个估算的成本量度,其中包含了对每步处理所估算的 I/O、CPU、内存等资源耗用成本。

    Rows ( 基数/ 数据行数 )
    在这里插入图片描述
    查看每个对象是否生成正确的行数?

    解决基数问题的建议 :
    在这里插入图片描述

  • 相关阅读:
    对话框
    windows系统下,C++统计进程内存使用情况
    c++超详细引用篇【c++】
    VCS coverage
    vue学习第五天(9月8号)
    win 平台 使用bat命令 免密上传至Linux
    基于logistics回归的评分卡模型【相关理论】
    java计算机毕业设计网上拍卖系统源程序+mysql+系统+lw文档+远程调试
    容器 —— 背景知识
    【C进阶】之宏定义的扩展
  • 原文地址:https://blog.csdn.net/Ruishine/article/details/138045863