oracle的索引類型有:非唯一索引、唯一索引、位圖索引、局部有前綴分區索引、局部無前綴分區索引、全局有前綴分區索引、散列分區索引、基于函數的索引。索引需在表中插入數據后創建,唯一索引可用“create unique index”語句創建。
本教程操作環境:Windows7系統、Oracle 11g版、Dell G3電腦。
什么是索引?
- 索引是建立在表的一列或多個列上的輔助對象,目的是加快訪問表中的數據;
- Oracle存儲索引的數據結構是B*樹(平衡樹),位圖索引也是如此,只不過是葉子節點不同B*數索引;
-
索引由根節點、分支節點和葉子節點組成,上級索引塊包含下級索引塊的索引數據,葉節點包含索引數據和確定行實際位置的rowid。
索引說明
1)索引是數據庫對象之一,用于加快數據的檢索,類似于書籍的索引。在數據庫中索引可以減少數據庫程序查詢結果時需要讀取的數據量,類似于在書籍中我們利用索引可以不用翻閱整本書即可找到想要的信息。
2)索引是建立在表上的可選對象;索引的關鍵在于通過一組排序后的索引鍵來取代默認的全表掃描檢索方式,從而提高檢索效率
3)索引在邏輯上和物理上都與相關的表和數據無關,當創建或者刪除一個索引時,不會影響基本的表;
4)索引一旦建立,在表上進行DML操作時(例如在執行插入、修改或者刪除相關操作時),oracle會自動管理索引,索引刪除,不會對表產生影響
5)索引對用戶是透明的,無論表上是否有索引,sql語句的用法不變
6)oracle創建主鍵時會自動在該列上創建索引
使用索引的目的:
- 加快查詢速度
- 減少I/O操作
- 消除磁盤排序(索引能加快排序速度)
何時使用索引:
- 查詢返回的記錄數 排序表
- 表的碎片較多(頻繁增加、刪除)
索引的種類
- 非唯一索引(最常用)
- 唯一索引
- 位圖索引
- 局部有前綴分區索引
- 局部無前綴分區索引
- 全局有前綴分區索引
- 散列分區索引
- 基于函數的索引
管理索引的準則
- 在表中插入數據后創建索引
- 在用SQL*Loader或import工具插入或裝載數據后,建立索引比較有效;
索引正確的表和列
- 經常檢索排序大表中40%或非排序表7%的行,建議建索引;
- 為了改善多表關聯,索引列用于聯結;
- 列中的值相對比較唯一;
- 取值范圍(大:B*樹索引,小:位圖索引);
- Date型列一般適合基于函數的索引;
- 列中有許多空值,不適合建立索引
為性能而安排索引列
- 經常一起使用多個字段檢索記錄,組合索引比單索引更有效;
- 把最常用的列放在最前面,例:dx_groupid_serv_id(groupid,serv_id),在where條件中使用groupid或groupid,serv_id,查詢將使用索引,若僅用到serv_id字段,則索引無效;
合并/拆分不必要的索引。
限制每個表索引的數量
-
一個表可以有幾百個索引(你會這樣做嗎?),但是對于頻繁插入和更新表,索引越多系統CPU,I/O負擔就越重;
-
建議每張表不超過5個索引。
刪除不再需要的索引
-
索引無效,集中表現在該使用基于函數的索引或位圖索引,而使用了B*樹索引;
-
應用中的查詢不使用索引;
-
重建索引之前必須先刪除索引,若用alter index … rebuild重建索引,則不必刪除索引。
索引數據塊空間使用
- 創建索引時指定表空間,特別是在建立主鍵時,應明確指定表空間;
- 合理設定pctfress,注意:不能給索引指定pctused;
- 估計索引的大小和合理地設置存儲參數,默認為表空間大小,或initial與next設置成一樣大。
考慮并行創建索引
- 對大表可以采用并行創建索引,在并行創建索引時,存儲參數被每個查詢服務器進程分別使用,例如:initial為1M,并行度為8,則創建索引期間至少要消耗8M空間;
考慮用nologging創建索引
- 對大表創建索引可以使用nologging來減少重做日志;
- 節省重做日志文件的空間;
- 縮短創建索引的時間;
- 改善了并行創建大索引時的性能。
怎樣建立最佳索引?
明確地創建索引
create?index?index_name?on?table_name(field_name) tablespace?tablespace_name pctfree?5 initrans?2 maxtrans?255 storage ( minextents?1 maxextents?16382 pctincrease?0 );
創建基于函數的索引:
常用與UPPER、LOWER、TO_CHAR(date)等函數分類上,例:
create?index?idx_func?on?emp?(UPPER(ename))?tablespace?tablespace_name;
創建位圖索引:
對基數較小,且基數相對穩定的列建立索引時,首先應該考慮位圖索引,例:
create?bitmap?index?idx_bitm?on?class?(classno)?tablespace?tablespace_name;
明確地創建唯一索引
可以用create unique index語句來創建唯一索引,例:
create?unique?index?dept_unique_idx?on?dept(dept_no)?tablespace?idx_1;
創建與約束相關的索引
可以用using index字句,為與unique和primary key約束相關的字段創建索引,例如:
alter?table?table_name add?constraint?PK_primary_keyname?primary?key?(field_name) using?index?tablespace?tablespace_name;
如何創建局部分區索引
- 基礎表必須是分區表;
- 分區數量與基礎表相同;
- 每個索引分區的子分區數量與相應的基礎表分區相同;
- 基礎表的子分區中的行的索引項,被存儲在該索引的相應的子分區中,例如:
Create?Index?TG_CDR04_SERV_ID_IDX?On?TG_CDR04(SERV_ID) Pctfree?5 Tablespace?TBS_AK01_IDX Storage?( MaxExtents?32768 PctIncrease?0 FreeLists?1 FreeList?Groups?1 ) local /
如何創建范圍分區的全局索引
基礎表可以是全局表和分區表。
create?index?idx_start_date?on?tg_cdr01(start_date) global?partition?by?range(start_date) (partition?p01_idx?vlaues?less?than?(‘0106’) partition?p01_idx?vlaues?less?than?(‘0111’) … partition?p01_idx?vlaues?less?than?(‘0401’?)) /
重建現存的索引
重建現存的索引的當前時刻不會影響查詢;
重建索引可以刪除額外的數據塊;
提高索引查詢效率;
alter?index?idx_name?rebuild?nologging;
對于分區索引:
alter?index?idx_name?rebuild?partition?partiton_name?nologging;
要刪除索引的原因
- 不再需要的索引;
- 索引沒有針對其相關的表所發布的查詢提供所期望的性能改善;
- 應用沒有用該索引來查詢數據;
- 該索引無效,必須在重建之前刪除該索引;
- 該索引已經變的太碎了,必須在重建之前刪除該索引;
- 語句:
drop index idx_name;
drop index idx_name drop partition partition_name;
建立索引的代價
基礎表維護時,系統要同時維護索引,不合理的索引將嚴重影響系統資源,主要表現在CPU和I/O上;
插入、更新、刪除數據產生大量db file sequential read鎖等待;
一個表中有幾百萬條數據,對某個字段加了索引,但是查詢時性能并沒有什么提高,這主要可能是oracle的索引限制造成的。
oracle的索引有一些索引限制,在這些索引限制發生的情況下,即使已經加了索引,oracle還是會執行一次全表掃描,查詢的性能不會比不加索引有所提高,反而可能由于數據庫維護索引的系統開銷造成性能更差。
擴展知識:常見的索引限制問題
1、使用不等于操作符(, !=)
下面這種情況,即使在列dept_id有一個索引,查詢語句仍然執行一次全表掃描
select?*?from?dept?where?staff_num??1000;
但是開發中的確需要這樣的查詢,難道沒有解決問題的辦法了嗎?
有!
通過把用 or 語法替代不等號進行查詢,就可以使用索引,以避免全表掃描:上面的語句改成下面這樣的,就可以使用索引了。
select?*?from?dept?shere?staff_num??1000;
2、使用 is null 或 is not null
使用 is null 或is nuo null也會限制索引的使用,因為數據庫并沒有定義null值。如果被索引的列中有很多null,就不會使用這個索引(除非索引是一個位圖索引,關于位圖索引,會在以后的blog文章里做詳細解釋)。在sql語句中使用null會造成很多麻煩。
解決這個問題的辦法就是:建表時把需要索引的列定義為非空(not null)
3、使用函數
如果沒有使用基于函數的索引,那么where子句中對存在索引的列使用函數時,會使優化器忽略掉這些索引。下面的查詢就不會使用索引:
select?*?from?staff?where?trunc(birthdate)?=?'01-MAY-82';
但是把函數應用在條件上,索引是可以生效的,把上面的語句改成下面的語句,就可以通過索引進行查找。
select?*?from?staff?where?birthdate?<p><strong>4、比較不匹配的數據類型</strong></p><p>比較不匹配的數據類型也是難于發現的性能問題之一。下面的例子中,dept_id是一個varchar2型的字段,在這個字段上有索引,但是下面的語句會執行全表掃描。</p><pre class="prettyprint">select?*?from?dept?where?dept_id?=?900198;
這是因為oracle會自動把where子句轉換成to_number(dept_id)=900198,就是3所說的情況,這樣就限制了索引的使用。把SQL語句改為如下形式就可以使用索引
select?*?from?dept?where?dept_id?=?'900198';
5、使用like子句
使用like子句查詢時,數據需要把所有的記錄都遍歷來進行判斷,索引不能發揮作用,這種情況也要盡量避免。
Like 的字符串中第一個字符如果是‘%’則用不到索引
Column1?like?‘aaa%’?是可以的 Column1?like?‘%aaa%’用不到
6、使用IN
盡管In寫法要比exists簡單一些,exists一般來說性能要比In要高的多?
用In還是用Exists的時機?
當in的集合比較小的時候,或者用Exists無法用到選擇性高的索引的時候,用In要好,否則就要用Exists
例:
select?count(*)?from?person_info?where?xb?in?(select?xb_id?from?dic_sex); Select?count(*)?from?n_acntbasic?a?where?shbxdjm?=:a?and?exists(select?1?from?person_info?where?pid=a.pid?and?…); Select?*?from?person_info?where?zjhm=3101….;--將會對person_info全表掃描 Select?*?from?person_info?where?zjhm?=‘3101…’;--才能用到索引
假定TEST表的dt字段是date類型的并且對dt建了索引。
如果要查‘20041010’一天的數據.下面的方法用不到索引
Select?*?from?test?where?to_char(dt,’yyyymmdd’)?=‘20041010’;
而以下將會用到索引。
select?*?from?test?where?dt?>=to_date(‘20041010’,’yyyymmdd’)?and?dt?<p><strong>7、</strong><strong>如果能不用到排序,則盡量避免排序。</strong></p><p>用到排序的情況有 <br> 集合操作。Union ,minus ,intersect等,注:union all 是不排序的。</p><pre class="prettyprint">Order?byGroup?byDistinctIn
有時候也會用到排序
確實要排序的時候也盡量要排序小數據量,盡量讓排序在內存中執行,有文章說,內存排序的速度是硬盤排序的1萬倍。
在排序的字段上創建索引,讓排序在內存中執行,加快排序速度。
8、在基于CBO的優化器(花費)下,表的統計數據過期。也可能導致不使用索引。
解決:執行表分析。獲取表的最新信息。
9、獲取的數據量過大,全部掃描效率更高
10、索引字段的值分散率太低,值太集中,如類型字段都是1,2, 狀態類型Y-有效/N-無效。這類型的字段最好別建索引。
盡管在這些字段上建立了索引,但對全表數據區分度不大。最后還是會全表掃描。
推薦教程:《Oracle教程》