zoukankan      html  css  js  c++  java
  • 【Oracle】从雇员表的年龄查询看CBO、索引和Hint的作用

    本例适用的Oracle版本:Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production

    不保证在其它版本的Oracle上也是同样效果。

    有一张雇员表含三个字段:

    create table emp(
    id int,
    name nvarchar2(20),
    age int,
    primary key(id));

    给它填充百万数据:

    insert into emp
    select 
        rownum,
        dbms_random.string('*',dbms_random.value(6,20)),
        dbms_random.value(18,65)
    from
        dual
    connect by level<1000001;

    然后我们要看看select name from emp where age=42这句SQL的cost是多少,注意现在没加索引(id上的索引除外,此索引与下面的测试无关)

    分别执行下面两句:

    explain plan for select name from emp where age=42;
    select * from table(dbms_xplan.display);

    不加索引的解释计划
    SQL> select * from table(dbms_xplan.display);
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    Plan hash value: 3956160932
    
    --------------------------------------------------------------------------
    | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    --------------------------------------------------------------------------
    |   0 | SELECT STATEMENT  |      | 23462 |   801K|  1523   (2)| 00:00:19 |
    |*  1 |  TABLE ACCESS FULL| EMP  | 23462 |   801K|  1523   (2)| 00:00:19 |
    --------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    
       1 - filter("AGE"=42)
    
    Note
    -----
       - dynamic sampling used for this statement (level=2)
    
    已选择17行。

    解释计划告诉我们Cost是1523,走的是全表查询。

    之后加age,name的组合索引
    create index idx_age_name on emp(age,name);

    再看看情况:

    SQL> select * from table(dbms_xplan.display);
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    Plan hash value: 2015333714
    
    --------------------------------------------------------------------------------
    -
    
    | Id  | Operation        | Name         | Rows  | Bytes | Cost (%CPU)| Time
    |
    
    --------------------------------------------------------------------------------
    -
    
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT |              | 23462 |   801K|   125   (0)| 00:00:02
    |
    
    |*  1 |  INDEX RANGE SCAN| IDX_AGE_NAME | 23462 |   801K|   125   (0)| 00:00:02
    |
    
    --------------------------------------------------------------------------------
    -
    
    
    Predicate Information (identified by operation id):
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    ---------------------------------------------------
    
       1 - access("AGE"=42)
    
    Note
    -----
       - dynamic sampling used for this statement (level=2)
    
    已选择17行。

    这回较好,索引起作用了,Cost降低到原来的十分之一,走的是索引范围扫描。

    之所以这个索引见效显著,是因为name和age都编到了索引中,在索引里找到了age,不用回表就找到了name。

    所以说,在查询条件、排序方式、显示列中出现的字段,如果确实需要,都可以编入到索引中。

    如果我们只对age加索引,看cbo是会选择回表还是全表查询。

    删除组合索引,只给age加单列索引
    SQL> drop index idx_age_name;
    
    索引已删除。
    
    SQL> create index idx_age on emp(age);
    
    索引已创建。
    
    SQL> explain plan for select name from emp where age=42;
    
    已解释。
    
    SQL> select * from table(dbms_xplan.display);
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    Plan hash value: 3956160932
    
    --------------------------------------------------------------------------
    | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    --------------------------------------------------------------------------
    |   0 | SELECT STATEMENT  |      | 23462 |   801K|  1523   (2)| 00:00:19 |
    |*  1 |  TABLE ACCESS FULL| EMP  | 23462 |   801K|  1523   (2)| 00:00:19 |
    --------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    
       1 - filter("AGE"=42)
    
    Note
    -----
       - dynamic sampling used for this statement (level=2)
    
    已选择17行。

    Cost又恢复了原值1523,查询方式还是走了全表查询,看来CBO认为全表查询比回表更省。

    我们可以试试用hint强制引导oracle使用idx_age这个索引:

    explain plan for select /*+ index(emp,idx_age)*/ name from emp where age=42;

    执行效果:

    SQL> explain plan for select /*+ index(emp,idx_age)*/ name from emp where age=42;
    
    已解释。
    
    SQL> select * from table(dbms_xplan.display);
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    Plan hash value: 977828616
    
    --------------------------------------------------------------------------------
    -------
    
    | Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Tim
    e     |
    
    --------------------------------------------------------------------------------
    -------
    
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |         | 23462 |   801K|  5471   (1)| 00:
    01:06 |
    
    |   1 |  TABLE ACCESS BY INDEX ROWID| EMP     | 23462 |   801K|  5471   (1)| 00:
    01:06 |
    
    |*  2 |   INDEX RANGE SCAN          | IDX_AGE | 23462 |       |    44   (0)| 00:
    00:01 |
    
    --------------------------------------------------------------------------------
    -------
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       2 - access("AGE"=42)
    
    Note
    -----
       - dynamic sampling used for this statement (level=2)
    
    已选择18行。

    上面已经出现了使用单列索引的迹象,但Cost不减反增。

    看来Cbo坚持全表查询的路子是对的。

    这个例子告诉我们:

    1.合理的索引能避免回表的发生;

    2.如果索引不合理,Cbo自会选择它认为的最合理的方式;

    3.hint只有合理才能提高效率。

    -END-

  • 相关阅读:
    微信公众平台接口获取时间戳为10位,java开发需转为13位
    redis实现哨兵机制
    redis配置主从复制
    C 语言字符 和字符串输出
    C 小写字母编程大写并输出
    C语言计算机器运行时间
    C 猜数游戏
    C 产生随机码 (输入数字来产生)
    C 产生随机码
    C 语言链表操作例程 (待完善)
  • 原文地址:https://www.cnblogs.com/heyang78/p/15306361.html
Copyright © 2011-2022 走看看