zoukankan      html  css  js  c++  java
  • ORACLE B-TREE(B树)索引

    内容简介:

    1.普通B-TREE 索引;

    2.唯一B-TREE 索引;

    3.复合索引;

    ORACLE 默认的索引类型为B-TREE 索引,表中的行标识符(ROWID)和行相关的列值被存储在一个平衡树的树状结构的索引块中;使用B-TREE索引有以下几个原因:

    提高SQL语句的性能;

    强制执行主键和唯一键约束的唯一性;

    减少通过主键和外键约束关联的父表和子表间潜在的锁定问题 ;

    1.普通B-TREE 索引

    在一张未建立任何索引的500万行人员信息表中根据人员ID查询人员信息

    select id,name,gender,homeaddr from th01 where id=998698;

    --------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    --------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 38 | 16715 (1)| 00:03:21 |

    |* 1 | TABLE ACCESS FULL| TH01 | 1 | 38 | 16715 (1)| 00:03:21 |

    --------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    1 - filter("ID"=998698)

    Statistics

    ----------------------------------------------------------

    61222 consistent gets

    61208 physical reads

    通过观察执行计划,CBO优化器执行了全表扫描,一致读取61222个块,61208个物理读,基于性能的考虑和表结构的分析,为其B-TREE索引:

    SQL> CREATE INDEX IND_TH01_ID ON TH01(ID) TABLESPACE TBS02;

    Index created.

    Elapsed: 00:00:33.03

    SQL> execute dbms_stats.gather_table_stats('sywu','th01',cascade=>true);

    PL/SQL procedure successfully completed.

    Elapsed: 00:00:04.17

    SQL> @/oracle/getind

    TABLE_NAME     INDEX_NAME    COLUMN_NAME    SIZE_GB    INDEX_TY   STATUS    LOGGING    DEGREE    NUM_ROWS    DISTINCT_KEYS

    ------------------------------ ------------------------------ ------------------------------ ---------- -------- -------- --------- ---------- -------- ---------- ------------- ---

    TH01                    IND_TH01_ID          ID                    .091796875       NORMAL VALID             YES               1            DISABLED        5000000   5000000

    显然对于高基数的列创建B-TREE索引是明智之选,对表进行分析后再次查询:

    SQL>select id,name,gender,homeaddr from th01 where id=998698;

    Elapsed: 00:00:00.00

    -------------------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    -------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 38 | 4 (0)| 00:00:01 |

    | 1 | TABLE ACCESS BY INDEX ROWID| TH01 | 1 | 38 | 4 (0)| 00:00:01 |

    |* 2 | INDEX RANGE SCAN | IND_TH01_ID | 1 | | 3 (0)| 00:00:01 |

    ------------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - access("ID"=998698)

    Statistics

    ----------------------------------------------------------

    5 consistent gets

    0 physical reads

    基于成本的考虑,CBO优化器选择了通过索引的方式读取数据,一致读取5个块,有效减少了额外的物理读;做个基于索引列的统计查询:

    SQL> select count(id) from th01;

    Elapsed: 00:00:00.15

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    -------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 6 | 3170 (1)| 00:00:39 |

    | 1 | SORT AGGREGATE | | 1 | 6 | | |

    | 2 | INDEX FAST FULL SCAN| IND_TH01_ID | 5000K| 28M| 3170 (1)| 00:00:39 |

    -------------------------------------------------------------------------------------

    Statistics

    ----------------------------------------------------------

    11810 consistent gets

    11794 physical reads

    CBO 优化器选择了全索引扫描,依旧消耗额外的资源;但当统计列发生改变时:

    SQL> select count(*) from th01;

    Elapsed: 00:00:00.14

    -------------------------------------------------------------------

    | Id | Operation | Name | Rows | Cost (%CPU)| Time |

    -------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 16707 (1)| 00:03:21 |

    | 1 | SORT AGGREGATE | | 1 | | |

    | 2 | TABLE ACCESS FULL| TH01 | 5000K| 16707 (1)| 00:03:21 |

    Statistics

    ----------------------------------------------------------

    61221 consistent gets

    61208 physical reads

    此时CBO 优化器选择了全表扫描,并消耗更多的资源;


    2.唯一B-TREE 索引:

    在为表创建(主键、唯一约束),ORACLE 会默认创建一个B-TREE索引,这样既保证了数据的唯一性也提高了数据的检索效率:

    SQL> alter table th01 add constraints cs_th01_uq unique(idcard);

    Table altered.

    Elapsed: 00:00:56.11

    TABLE_NAME      INDEX_NAME     COLUMN_NAME     SIZE_GB    INDEX_TY      STATUS     LOGGING        DEGREE    COMPRESS    NUM_ROWS    DISTINCT_KEYS

    ------------------------------ ------------------------------ ------------------------------ ---------- -------- -------- --------- ---------- -------- ---------- ------------- ---

    TH01             CS_TH01_UQ                  IDCARD                .15625               NORMAL   VALID              YES                  1              DISABLED    4969898           4969898

    IDCARD查询人员信息:

    ------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 35 | 3 (0)| 00:00:01 |

    | 1 | TABLE ACCESS BY INDEX ROWID| TH01 | 1 | 35 | 3 (0)| 00:00:01 |

    |* 2 | INDEX UNIQUE SCAN | CS_TH01_UQ | 1 | | 2 (0)| 00:00:01 |

    ------------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - access("IDCARD"='562456864646565545')

    Statistics

    ----------------------------------------------------------

    3 consistent gets

    0 physical reads

    做为默认创建的索引,它依旧能高效的工作,ORACLE是不允许将其作为独立的索引删除的,只能通过删除约束的方式删除;对于主键,它的情况要复杂些,因为还要考虑外键的约束;基于这种方式创建的索引,当约束被删除时还要重新创建索引,显然在一张大表上花费的代价和时间是昂贵的; so,采用如下的方式合理的建立约束和索引:

    SQL> alter table th01 add constraints CS_TH01_UQ unique(idcard)

    2* using index tablespace tbs03 ;

    Table altered.

    Elapsed: 00:00:59.27

    倘若有一天业务发生了改变,唯一约束已经不是必须的,但索引是必须的,那只需要删除约束保留索引:

    SQL> alter table th01 drop constraints CS_TH01_UQ keep index;

    Table altered.

    Elapsed: 00:00:00.01

    再次通过IDCARD 查询人员信息:

    SQL> select id,name,idcard from th01 where idcard='56234256878945';

    ------------------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    ------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 35 | 3 (0)| 00:00:01 |

    | 1 | TABLE ACCESS BY INDEX ROWID| TH01 | 1 | 35 | 3 (0)| 00:00:01 |

    |* 2 | INDEX UNIQUE SCAN | CS_TH01_UQ | 1 | | 2 (0)| 00:00:01 |

    ------------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - access("IDCARD"='56234256878945')

    Statistics

    ----------------------------------------------------------

    1 recursive calls

    0 db block gets

    3 consistent gets


    只创建唯一索引:

    SQL> create unique index ind_th02 on th02(idcard) tablespace tbs03;

    唯一索引与唯一约束相比,唯一索引只创建索引而不添加约束,它保证索引列数值唯一性,允许有空值;


    3.复合索引:

    可以在多个列上创建索引,其结果称为复合索引或组合索引:

    SQL> create index ind_th01_union on th01(id,name,idcard) tablespace tbs03;

    当查询的WHERE子句引用了索引的所有列或者只是前导列,CBO会使用复合索引

    SQL>select id,name,idcard from th01 where idcard='9876534655635666' and id=68956254 and name='张三';

    -----------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    -----------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 35 | 3 (0)| 00:00:01 |

    |* 1 | INDEX RANGE SCAN| IND_TH01_UNION | 1 | 35 | 3 (0)| 00:00:01 |

    -----------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    1 - access("ID"=68956254 AND "NAME"='张三' AND

    "IDCARD"='9876534655635666')

    Statistics

    ----------------------------------------------------------

    1 recursive calls

    0 db block gets

    3 consistent gets

    2 physical reads

    通过查询结果,对于之前创建的单列索引(IND_TH01_IDCS_TH01_UQ),优化器已不再使用;对于复合索引( IND_TH01_UNION) 来说, id | id, name| id,name,idcard 三个组合都被认为是前导列,假如我只是在WHERE 子句中引用了第一个主导列ID,那么优化器依旧会选择复合索引(IND_TH01_UNION )忽略单列索引( IND_TH01_ID)

    SQL> select * from th01 where id=698698;

    ----------------------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    ----------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 77 | 4 (0)| 00:00:01 |

    | 1 | TABLE ACCESS BY INDEX ROWID| TH01 | 1 | 77 | 4 (0)| 00:00:01 |

    |* 2 | INDEX RANGE SCAN | IND_TH01_UNION | 1 | | 3 (0)| 00:00:01 |

    ----------------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - access("ID"=698698)

    Statistics

    ----------------------------------------------------------

    0 db block gets

    5 consistent gets

     

    假如where 子句中的条件不符合复合索引前导列的要求,那么优化器会忽略复合索引( IND_TH01_UNION)选择单列索引(CS_TH01_UQ):

    SQL> select * from th01 where idcard='5623546566564665';

    ------------------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    ------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 77 | 3 (0)| 00:00:01 |

    | 1 | TABLE ACCESS BY INDEX ROWID| TH01 | 1 | 77 | 3 (0)| 00:00:01 |

    |* 2 | INDEX UNIQUE SCAN | CS_TH01_UQ | 1 | | 2 (0)| 00:00:01 |

    ------------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - access("IDCARD"='5623546566564665')

    Statistics

    ----------------------------------------------------------

    1 recursive calls

    0 db block gets

    3 consistent gets

    1 physical reads

    对于此时的查询条件 where idcard='5623546566564665' 已经不再符合( id | id, name| id,name,idcard) 复合索引前导列的条件,优化器选择单列索引( CS_TH01_UQ);又假如,我的WHERE 子句条件符合复合索引前导列要求但不是全部满足:

    SQL> select * from th01 where id=698698 and name='张三';

    ----------------------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    ----------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 77 | 4 (0)| 00:00:01 |

    | 1 | TABLE ACCESS BY INDEX ROWID| TH01 | 1 | 77 | 4 (0)| 00:00:01 |

    |* 2 | INDEX RANGE SCAN | IND_TH01_UNION | 1 | | 3 (0)| 00:00:01 |

    ----------------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - access("ID"=698698 AND "NAME"='张三')

    Statistics

    ----------------------------------------------------------

    3 consistent gets

    0 physical reads

    通过分析(where id=698698 and name='张三' )符合复合索引前导列要求,优化器选择复合索引( IND_TH01_UNION) 忽略单列索引( IND_TH01_ID);

    创建复合索引时,排序是个很大的问题,ORACLE 建议将最频繁访问的列放在索引中最靠前的位置,应避免使用低基数的列作为复合索引的前导列.

     




    Hope is personal,hope is something that no one can give to you,you have to choose to believe in hope,you have to make it yourself..
  • 相关阅读:
    Codeforces 897 B.Chtholly's request-思维题(处理前一半)
    Codeforces 897 A.Scarborough Fair-字符替换
    51nod 1649.齐头并进-最短路(Dijkstra)
    牛客网 牛客练习赛7 B.购物-STL(priority_queue)
    牛客网 牛客练习赛7 A.骰子的游戏
    hdu 4737 A Bit Fun 尺取法
    Educational Codeforces Round 15 D. Road to Post Office 数学
    Educational Codeforces Round 15 A, B , C 暴力 , map , 二分
    tyvj 1402 乌龟棋 dp
    vijos 1057 盖房子 dp 最大子正方形
  • 原文地址:https://www.cnblogs.com/lanston/p/3485466.html
Copyright © 2011-2022 走看看