ORACLE B-TREE(B树)索引
內(nèi)容簡介:
1.普通B-TREE 索引;
2.唯一B-TREE 索引;
3.復(fù)合索引;
ORACLE 默認(rèn)的索引類型為B-TREE 索引,表中的行標(biāo)識符(ROWID)和行相關(guān)的列值被存儲在一個平衡樹的樹狀結(jié)構(gòu)的索引塊中;使用B-TREE索引有以下幾個原因:
? 提高SQL語句的性能;
? 強(qiáng)制執(zhí)行主鍵和唯一鍵約束的唯一性;
? 減少通過主鍵和外鍵約束關(guān)聯(lián)的父表和子表間潛在的鎖定問題 ;
1.普通B-TREE 索引
在一張未建立任何索引的500萬行人員信息表中根據(jù)人員ID查詢?nèi)藛T信息
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
通過觀察執(zhí)行計劃,CBO優(yōu)化器執(zhí)行了全表掃描,一致讀取61222個塊,61208個物理讀,基于性能的考慮和表結(jié)構(gòu)的分析,為其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
顯然對于高基數(shù)的列創(chuàng)建B-TREE索引是明智之選,對表進(jìn)行分析后再次查詢:
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優(yōu)化器選擇了通過索引的方式讀取數(shù)據(jù),一致讀取5個塊,有效減少了額外的物理讀;做個基于索引列的統(tǒng)計查詢:
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 優(yōu)化器選擇了全索引掃描,依舊消耗額外的資源;但當(dāng)統(tǒng)計列發(fā)生改變時:
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 優(yōu)化器選擇了全表掃描,并消耗更多的資源;
2.唯一B-TREE 索引:
在為表創(chuàng)建(主鍵、唯一約束)時,ORACLE 會默認(rèn)創(chuàng)建一個B-TREE索引,這樣既保證了數(shù)據(jù)的唯一性也提高了數(shù)據(jù)的檢索效率:
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查詢?nèi)藛T信息:
------------------------------------------------------------------------------------------
| 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
做為默認(rèn)創(chuàng)建的索引,它依舊能高效的工作,但ORACLE是不允許將其作為獨(dú)立的索引刪除的,只能通過刪除約束的方式刪除;對于主鍵,它的情況要復(fù)雜些,因?yàn)檫€要考慮外鍵的約束;基于這種方式創(chuàng)建的索引,當(dāng)約束被刪除時還要重新創(chuàng)建索引,顯然在一張大表上花費(fèi)的代價和時間是昂貴的; so,采用如下的方式合理的建立約束和索引:
SQL> alter table th01 add constraints CS_TH01_UQ unique(idcard)
2* using index tablespace tbs03 ;
Table altered.
Elapsed: 00:00:59.27
倘若有一天業(yè)務(wù)發(fā)生了改變,唯一約束已經(jīng)不是必須的,但索引是必須的,那只需要刪除約束保留索引:
SQL> alter table th01 drop constraints CS_TH01_UQ keep index;
Table altered.
Elapsed: 00:00:00.01
再次通過IDCARD 查詢?nèi)藛T信息:
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
只創(chuàng)建唯一索引:
SQL> create unique index ind_th02 on th02(idcard) tablespace tbs03;
唯一索引與唯一約束相比,唯一索引只創(chuàng)建索引而不添加約束,它保證索引列數(shù)值唯一性,允許有空值;
3.復(fù)合索引:
可以在多個列上創(chuàng)建索引,其結(jié)果稱為復(fù)合索引或組合索引:
SQL> create index ind_th01_union on th01(id,name,idcard) tablespace tbs03;
當(dāng)查詢的WHERE子句引用了索引的所有列或者只是前導(dǎo)列,CBO會使用復(fù)合索引
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
通過查詢結(jié)果,對于之前創(chuàng)建的單列索引(IND_TH01_ID、CS_TH01_UQ),優(yōu)化器已不再使用;對于復(fù)合索引( IND_TH01_UNION) 來說, id | id, name| id,name,idcard 三個組合都被認(rèn)為是前導(dǎo)列,假如我只是在WHERE 子句中引用了第一個主導(dǎo)列ID,那么優(yōu)化器依舊會選擇復(fù)合索引(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 子句中的條件不符合復(fù)合索引前導(dǎo)列的要求,那么優(yōu)化器會忽略復(fù)合索引( 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' 已經(jīng)不再符合( id | id, name| id,name,idcard) 復(fù)合索引前導(dǎo)列的條件,優(yōu)化器選擇單列索引( CS_TH01_UQ);又假如,我的WHERE 子句條件符合復(fù)合索引前導(dǎo)列要求但不是全部滿足:
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='張三' )符合復(fù)合索引前導(dǎo)列要求,優(yōu)化器選擇復(fù)合索引( IND_TH01_UNION) 忽略單列索引( IND_TH01_ID);
創(chuàng)建復(fù)合索引時,排序是個很大的問題,ORACLE 建議將最頻繁訪問的列放在索引中最靠前的位置,應(yīng)避免使用低基數(shù)的列作為復(fù)合索引的前導(dǎo)列.
?
轉(zhuǎn)載于:https://www.cnblogs.com/lanston/p/3485466.html
總結(jié)
以上是生活随笔為你收集整理的ORACLE B-TREE(B树)索引的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: tomcat是否有必要配置环境变量(摘)
- 下一篇: 第一节 接口概述 [转贴]