zoukankan      html  css  js  c++  java
  • 常见的几种索引扫描类型

    一、如何查看SQL语句的执行计划


    1、在SQL*PLUS环境中
     
    执行SET AUTOTRACE ON 可以打开启动自动跟踪功能,如下(下面的红色字体是敲入的命令):
    ChenZw> set autotrace on
    ChenZw> select * from emp;
     
         EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO
    ---------- ---------- --------- ---------- -------------- ---------- ---------- ----------
          7369 SMITH      CLERK           7902 17-12月-80            800                    20
          7499 ALLEN      SALESMAN        7698 20-2月 -81           1600        300         30
          7521 WARD       SALESMAN        7698 22-2月 -81           1250        500         30
          7566 JONES      MANAGER         7839 02-4月 -81           2975                    20
          7654 MARTIN     SALESMAN        7698 28-9月 -81           1250       1400         30
          7698 BLAKE      MANAGER         7839 01-5月 -81           2850                    30
          7782 CLARK      MANAGER         7839 09-6月 -81           2450                    10
          7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20
          7839 KING       PRESIDENT            17-11月-81           5000                    10
          7844 TURNER     SALESMAN        7698 08-9月 -81           1500          0         30
          7876 ADAMS      CLERK           7788 23-5月 -87           1100                    20
          7900 JAMES      CLERK           7698 03-12月-81            950                    30
          7902 FORD       ANALYST         7566 03-12月-81           3000                    20
          7934 MILLER     CLERK           7782 23-1月 -82           1300                    10
     
    已选择14行。
     
     
    执行计划
    ----------------------------------------------------------
    Plan hash value: 3956160932
     
    --------------------------------------------------------------------------
    | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    --------------------------------------------------------------------------
    |   0 | SELECT STATEMENT  |      |    14 |   518 |     3   (0)| 00:00:01 |
    |   1 |  TABLE ACCESS FULL| EMP  |    14 |   518 |     3   (0)| 00:00:01 |
    --------------------------------------------------------------------------
     
     
    统计信息
    ----------------------------------------------------------
            476  recursive calls
              0  db block gets
             90  consistent gets
              9  physical reads
              0  redo size
           1423  bytes sent via SQL*Net to client
            416  bytes received via SQL*Net from client
              2  SQL*Net roundtrips to/from client
              7  sorts (memory)
              0  sorts (disk)
             14  rows processed
        如同describe命令可以省略为desc一样,set autotrace off命令也可以省略为set autot off。如果,执行计划中的行太宽的话,可以执行如下命令,将行的宽度设置为200,命令如下:
    ChenZw> set linesize 200
    ChenZw>
     
    2、在PL/SQL Developer软件中
     
    在PL/SQL Developer窗口中,点击“文件”的“新建”菜单中选择“SQL窗口”,在里面输入自己要执行的SQL,例如上面的
    SELECT * FROM EMP;
    然后点击“F5”按钮,系统会自动弹出“解释计划窗口”,并且将该SQL的执行计划在“解释计划窗口”中显示出来
    或者直接创建“解释计划窗口”也可以进入解释计划窗口。
        现在回过头来看这个SQL语句,使用的是“TABLE ACCESS FULL”的方式进行查询,那么优化器为什么会使用全表查询呢?有这样几个情况下,数据库会选择全表索引的。
    第一、高度并发的时候,如果在DBA_TABLES视图里面的DEGREE(The number of threads per instance for scanning the table)字段的值比较高的时候,优化器会选择全表扫描的方式。
    第二、在该表上,如果没有索引的时候,正所谓“巧妇难为无米之炊”,优化器也只能选择全表扫描FULL TABLE SCAN的方式了。
    第三、查询大数据量的时候,比如上面的SQL语句,优化器一看后面一个WHERE条件都没有,或者优化器认为该次查询将会查询到大部分的数据,则会使用全表扫描,这种情况下,就算是该SQL能够用到索引,也不会使用的。
    第四种,查询小标的时候,在初始化参数里面有一个DB_FILE_MULTIBLOCK_READ_COUNT,这个参数指定了每次读数据块的数量,但是如果发现当前要查询的表的数据表小于该参数的值,优化器也会选择全表扫描的。
     
    二、SQL语句的几种常见的索引扫描类型
     
        首先,有一个概念,就是除了获取索引本身的值之外,使用索引最终还是要使用表的ROWID进行获取数据的。也就是TABLE ACCESS BY ROWID。什么是ROWID呢?下面的查询:
     
    ChenZw> select t.rowid,t.* from emp t;
     
    ROWID              EMPNO ENAME  JOB       MGR HIREDATE    SAL  COMM DEPTNO
    ------------------ ----- ------ ------------- --------------- ----- ------
    AAAQ+jAAEAAAAAeAAA  7369 SMITH  CLERK    7902 17-12月-80  800        20
    AAAQ+jAAEAAAAAeAAB  7499 ALLEN  SALESMAN 7698 20-2月 -81 1600   300  30
    AAAQ+jAAEAAAAAeAAC  7521 WARD   SALESMAN 7698 22-2月 -81 1250   500  30
    AAAQ+jAAEAAAAAeAAD  7566 JONES  MANAGER  7839 02-4月 -81 2975        20
    AAAQ+jAAEAAAAAeAAE  7654 MARTIN SALESMAN 7698 28-9月 -81 1250  1400  30
    AAAQ+jAAEAAAAAeAAF  7698 BLAKE  MANAGER  7839 01-5月 -81 2850        30
    AAAQ+jAAEAAAAAeAAG  7782 CLARK  MANAGER  7839 09-6月 -81 2450        10
    AAAQ+jAAEAAAAAeAAH  7788 SCOTT  ANALYST  7566 19-4月 -87 3000        20
    AAAQ+jAAEAAAAAeAAI  7839 KING   PRESIDENT     17-11月-81 5000        10
    AAAQ+jAAEAAAAAeAAJ  7844 TURNER SALESMAN 7698 08-9月 -81 1500     0  30
    AAAQ+jAAEAAAAAeAAK  7876 ADAMS  CLERK    7788 23-5月 -87 1100        20
    AAAQ+jAAEAAAAAeAAL  7900 JAMES  CLERK    7698 03-12月-81  950        30
    AAAQ+jAAEAAAAAeAAM  7902 FORD   ANALYST  7566 03-12月-81 3000        20
    AAAQ+jAAEAAAAAeAAN  7934 MILLER CLERK    7782 23-1月 -82 1300        10
     
        ROWID字段中保存了很多信息,例如所在数据块、数据文件、行在数据块中的信息等等,可以非常方便的通过ROWID的信息找到该记录所在的位置,在oracle中,使用ROWID来查找某条记录是最快的。在使用索引的时候,如果不是查询索引本身的值(例如主键),而是查询主键所在行的其他字段信息的话,Oracle也是首先得到ROWID的信息,然后再去找到其他字段的信息,并且返回。
     
    在网上找到这样几种最常见的扫描的类型:
     
    第一种:index unique  scan
        索引唯一扫描,当可以优化器发现某个查询条件可以利用到主键、唯一键、具有外键约束的列,或者只是访问其中某行索引所在的数据的时候,优化器会选择这种扫描类型。
     
    第二种:index range scan
        索引范围扫描,当优化器发现在UNIQUE列上使用了大于、小于、大于等于、小于等于以及BETWEEN等就会使用范围扫描,在组合列上只使用部分进行查询,导致查询出多行数据。对非唯一的索引列上进行任何活动都会使用index range scan
     
    第三种:index full scan
        全索引扫描,如果要查询的数据可以全部从索引中获取,则使用全索引扫描。
     
    第四种:index fast full scan
        索引快速扫描,扫描索引中的全部的数据块,与全索引扫描的方式基本上类似。两者之间的明显的区别是,索引快速扫描对查询的数据不进行排序,数据返回的时候不是排序的。“在这种存取方法中,可以使用多块读功能,也可以使用并行读入,从而得到最大的吞吐量和缩短执行时间”。
     
    三、几种常见的索引类型的模拟
     
    创建测试表以及测试数据的SQL:
     
    CREATE TABLE TBL(
        USERID NUMBER(20),
        NAME VARCHAR2(20),
        DEPTNO NUMBER(20)
    )
    ;
    --模拟数据
    BEGIN
        FOR I IN 0..1000 LOOP
            INSERT INTO TBL VALUES(
            I,CONCAT('TBL',I),MOD(I,2));
        END LOOP;
    END;
     
    SELECT * FROM TBL;
     
    1)TABLE ACCESS BY USER ROWID
    执行下面的语句得到几个ROWID:
    ChenZw> select rowid,tbl.* from tbl where rownum<10;
     
    ROWID                  USERID NAME                     DEPTNO
    ------------------ ---------- -------------------- ----------
    AAAUhAAAEAAAAIcAAA        796 TBL796                        0
    AAAUhAAAEAAAAIcAAB        797 TBL797                        1
    AAAUhAAAEAAAAIcAAC        798 TBL798                        0
    AAAUhAAAEAAAAIcAAD        799 TBL799                        1
    AAAUhAAAEAAAAIcAAE        800 TBL800                        0
    AAAUhAAAEAAAAIcAAF        801 TBL801                        1
    AAAUhAAAEAAAAIcAAG        802 TBL802                        0
    AAAUhAAAEAAAAIcAAH        803 TBL803                        1
    AAAUhAAAEAAAAIcAAI        804 TBL804                        0
     
    然后带着rowid去查询,看这个时候的执行计划:
    ChenZw> select * from tbl where rowid = 'AAAUhAAAEAAAAIcAAA';
    执行计划
    ----------------------------------------------------------
    Plan hash value: 2623095911
     
    -----------------------------------------------------------------------------------
    | Id  | Operation                  | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    -----------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT           |      |     1 |    50 |     1   (0)| 00:00:01 |
    |   1 |  TABLE ACCESS BY USER ROWID| TBL  |     1 |    50 |     1   (0)| 00:00:01 |
    -----------------------------------------------------------------------------------
    上面的TABLE ACCESS BY USER ROWID是一种效率最高的获取数据的方法。
     
    2)index unique  scan
    修改上面的表,增加一个主键,等于自动会增加一个唯一键约束:
    ALTER TABLE TBL ADD CONSTRAINT TBLID PRIMARY KEY (USERID);
    执行下面的SQL语句:
    ChenZw> select * from tbl where userid = 100;
    执行计划
    ----------------------------------------------------------
    Plan hash value: 1167568666
     
    -------------------------------------------------------------------------------------
    | Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
    -------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |       |     1 |    38 |     2   (0)| 00:00:01 |
    |   1 |  TABLE ACCESS BY INDEX ROWID| TBL   |     1 |    38 |     2   (0)| 00:00:01 |
    |*  2 |   INDEX UNIQUE SCAN         | TBLID |     1 |       |     1   (0)| 00:00:01 |
    -------------------------------------------------------------------------------------
     
    从上面的执行计划可以看出,优化器首先是根据为刚才建立的索引TBLID来找到100的ROWID,然后根据ROWID去找到100所在的行数据。
     
    2)index full scan
    为上面的表增加一个联合索引,在TBL.NAME和TBL.DEPTNO两个列上,SQL如下:
    CREATE INDEX INDEX_TBL_NAME_DEPTNO ON TBL(NAME,DEPTNO);
     
    3)index range scan
    执行下面的语句:
    ChenZw> select * from tbl where userid between 10 and 100;
    已选择91行。
    执行计划
    ----------------------------------------------------------
    Plan hash value: 2314926374
     
    -------------------------------------------------------------------------------------
    | Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
    -------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |       |    91 |  3458 |     3   (0)| 00:00:01 |
    |   1 |  TABLE ACCESS BY INDEX ROWID| TBL   |    91 |  3458 |     3   (0)| 00:00:01 |
    |*  2 |   INDEX RANGE SCAN          | TBLID |    91 |       |     2   (0)| 00:00:01 |
    -------------------------------------------------------------------------------------
    在索引列上使用大于等于,小于等于,between等的时候,优化器会首先在索引列上进行操作,进行一个索引范围扫描得到ROWID之后,再根据ROWID来找到相应的数据。
     
    这个里面有一个很有意思的问题:
    ChenZw> select * from tbl where userid between 10 and 100 order by 1;
    已选择91行。
    执行计划
    ----------------------------------------------------------
    Plan hash value: 2314926374
     
    -------------------------------------------------------------------------------------
    | Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
    -------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |       |    91 |  3458 |     3   (0)| 00:00:01 |
    |   1 |  TABLE ACCESS BY INDEX ROWID| TBL   |    91 |  3458 |     3   (0)| 00:00:01 |
    |*  2 |   INDEX RANGE SCAN          | TBLID |    91 |       |     2   (0)| 00:00:01 |
    -------------------------------------------------------------------------------------
    看执行计划,我们虽然在SQL语句指定了需要将结果集进行排序,但是在执行计划中,并没有进行排序的动作,这个是为什么呢?
    ChenZw> select * from tbl where userid between 10 and 100 order by 1 desc;
    已选择91行。
    执行计划
    ----------------------------------------------------------
    Plan hash value: 4193471386
     
    --------------------------------------------------------------------------------------
    | Id  | Operation                    | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
    --------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT             |       |    91 |  3458 |     3   (0)| 00:00:01 |
    |   1 |  TABLE ACCESS BY INDEX ROWID | TBL   |    91 |  3458 |     3   (0)| 00:00:01 |
    |*  2 |   INDEX RANGE SCAN DESCENDING| TBLID |    91 |       |     2   (0)| 00:00:01 |
    --------------------------------------------------------------------------------------
    这个执行计划和上面的执行计划有什么区别呢?其中第一个执行计划是索引范围扫描(INDEX RANGE SCAN),但是第二个执行计划是索引范围降序扫描(INDEX RANGE SCAN DESCENDING)。由此可以看出对于唯一索引,ORACLE是已经进行了正序排列了。也就是唯一索引在进行排序的时候消耗不是很大的,因为它在保存的时候就按照升序进行保存的。
     
    4)index fast full scan
    假如我们只需要得到所有的USERID的时候,优化器会如何进行查询呢?我们要查的USERID是全部在索引里面的,执行一下下面的语句,得到下面的执行计划:
    ChenZw> select userid from tbl;
    已选择100001行。
    执行计划
    ----------------------------------------------------------
    Plan hash value: 3798421822
     
    ------------------------------------------------------------------------------
    | Id  | Operation            | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT     |       |   115K|  1471K|    63   (2)| 00:00:01 |
    |   1 |  INDEX FAST FULL SCAN| TBLID |   115K|  1471K|    63   (2)| 00:00:01 |
    ------------------------------------------------------------------------------
    看到上面的执行计划是索引快速扫描(INDEX FAST FULL SCAN),因为需要得到的所有的数据都是在索引里面的,因此只需要将所需要的数据从索引中取出来就ok了。
  • 相关阅读:
    web服务器-Apache
    nginx优化
    nginx下载限速
    nginx-URL重写
    HDU 5358 First One 求和(序列求和,优化)
    HDU 5360 Hiking 登山 (优先队列,排序)
    HDU 5353 Average 糖果分配(模拟,图)
    UVALive 4128 Steam Roller 蒸汽式压路机(最短路,变形) WA中。。。。。
    HDU 5348 MZL's endless loop 给边定向(欧拉回路,最大流)
    HDU 5344 MZL's xor (水题)
  • 原文地址:https://www.cnblogs.com/login2012/p/5871123.html
Copyright © 2011-2022 走看看