1.什么是索引
索引是建立在表的一列或多個列上的輔助對象,目的是加快訪問表中的數據;
Oracle存儲索引的數據結構是B*樹,位圖索引也是如此,只不過是葉子節點不同B*數索引;
索引由根節點、分支節點和葉子節點組成,上級索引塊包含下級索引塊的索引數據,葉節點包含索引數據和確定行實際位置的rowid。
使用索引的目的
加快查詢速度
減少I/O操作
消除磁盤排序
何時使用索引
查詢返回的記錄數
排序表<40%
非排序表 <7%
表的碎片較多(頻繁增加、刪除)
2.索引的種類
非唯一索引(最常用)
唯一索引
位圖索引
局部有前綴分區索引
局部無前綴分區索引
全局有前綴分區索引
散列分區索引
基於函數的索引
3.管理索引的准則
在表中插入數據后創建索引
在用SQL*Loader或import工具插入或裝載數據后,建立索引比較有效;
3.1索引正確的表和列
經常檢索排序大表中40%或非排序表7%的行,建議建索引;
。為了改善多表關聯,索引列用於聯結;
。列中的值相對比較唯一;
。取值范圍(大:B*樹索引,小:位圖索引);
。Date型列一般適合基於函數的索引;
。列中有許多空值,不適合建立索引
3.2為性能而安排索引列
。經常一起使用多個字段檢索記錄,組合索引比單索引更有效;
。把最常用的列放在最前面,例:dx_groupid_serv_id(groupid,serv_id),在where條件中使用groupid或groupid,serv_id,查詢將使用索引,若僅用到serv_id字段,則索引無效;
3.3合並/拆分不必要的索引。
3.4限制每個表索引的數量
。一個表可以有幾百個索引(你會這樣做嗎?),但是對於頻繁插入和更新表,索引越多系統CPU,I/O負擔就越重;
。建議每張表不超過5個索引。
3.5刪除不再需要的索引
。索引無效,集中表現在該使用基於函數的索引或位圖索引,而使用了B*樹索引;
。應用中的查詢不使用索引;
。重建索引之前必須先刪除索引,若用alter index … rebuild重建索引,則不必刪除索引。
3.6索引數據塊空間使用
。創建索引時指定表空間,特別是在建立主鍵時,應明確指定表空間;
。合理設定pctfress,注意:不能給索引指定pctused;
。估計索引的大小和合理地設置存儲參數,默認為表空間大小,或initial與next設置成一樣大。
3.7考慮並行創建索引
。對大表可以采用並行創建索引,在並行創建索引時,存儲參數被每個查詢服務器進程分別使用,例如:initial為1M,並行度為8,則創建索引期間至少要消耗8M空間;
3.8考慮用nologging創建索引
。對大表創建索引可以使用nologging來減少重做日志;
。節省重做日志文件的空間;
。縮短創建索引的時間;
。改善了並行創建大索引時的性能。
4.怎樣建立最佳索引
明確地創建索引
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;
5.如何創建局部分區索引
。基礎表必須是分區表;
。分區數量與基礎表相同;
。每個索引分區的子分區數量與相應的基礎表分區相同;
。基礎表的子分區中的行的索引項,被存儲在該索引的相應的子分區中,例如:
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
/
6如何創建范圍分區的全局索引
。基礎表可以是全局表和分區表。
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;
7.要刪除索引的原因
。不再需要的索引;
。索引沒有針對其相關的表所發布的查詢提供所期望的性能改善;
。應用沒有用該索引來查詢數據;
。該索引無效,必須在重建之前刪除該索引;
。該索引已經變的太碎了,必須在重建之前刪除該索引;
。語句:drop index idx_name;drop index idx_name drop partition partition_name;
8.建立索引的代價
基礎表維護時,系統要同時維護索引,不合理的索引將嚴重影響系統資源,主要表現在CPU和I/O上;
插入、更新、刪除數據產生大量db file sequential read鎖等待;
一個表中有幾百萬條數據,對某個字段加了索引,但是查詢時性能並沒有什么提高,這主要可能是oracle的索引限制造成的。
oracle的索引有一些索引限制,在這些索引限制發生的情況下,即使已經加了索引,oracle還是會執行一次全表掃描,查詢的性能不會比不加索引有所提高,反而可能由於數據庫維護索引的系統開銷造成性能更差。
下面是一些常見的索引限制問題。
9、使用不等於操作符(<>, !=)
下面這種情況,即使在列dept_id有一個索引,查詢語句仍然執行一次全表掃描
select * from dept where staff_num <> 1000;
但是開發中的確需要這樣的查詢,難道沒有解決問題的辦法了嗎?
有!
通過把用 or 語法替代不等號進行查詢,就可以使用索引,以避免全表掃描:上面的語句改成下面這樣的,就可以使用索引了。
select * from dept shere staff_num < 1000 or dept_id > 1000;
10、使用 is null 或 is not null
使用 is null 或is nuo null也會限制索引的使用,因為數據庫並沒有定義null值。如果被索引的列中有很多null,就不會使用這個索引(除非索引是一個位圖索引,關於位圖索引,會在以后的blog文章里做詳細解釋)。在sql語句中使用null會造成很多麻煩。
解決這個問題的辦法就是:建表時把需要索引的列定義為非空(not null)
11、使用函數
如果沒有使用基於函數的索引,那么where子句中對存在索引的列使用函數時,會使優化器忽略掉這些索引。下面的查詢就不會使用索引:
select * from staff where trunc(birthdate) = '01-MAY-82';
但是把函數應用在條件上,索引是可以生效的,把上面的語句改成下面的語句,就可以通過索引進行查找。
select * from staff where birthdate < (to_date('01-MAY-82') + 0.9999);
12、比較不匹配的數據類型
比較不匹配的數據類型也是難於發現的性能問題之一。下面的例子中,dept_id是一個varchar2型的字段,在這個字段上有索引,但是下面的語句會執行全表掃描。
select * from dept where dept_id = 900198;
這是因為oracle會自動把where子句轉換成to_number(dept_id)=900198,就是3所說的情況,這樣就限制了索引的使用。把SQL語句改為如下形式就可以使用索引
select * from dept where dept_id = '900198';
13、使用like子句
使用like子句查詢時,數據需要把所有的記錄都遍歷來進行判斷,索引不能發揮作用,這種情況也要盡量避免。
Like 的字符串中第一個字符如果是‘%’則用不到索引
Column1 like ‘aaa%’ 是可以的
Column1 like ‘%aaa%’用不到
14.使用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 < to_date(‘20041010’,’yyyymmdd’) + 1 將會用到索引。
15.如果能不用到排序,則盡量避免排序。
用到排序的情況有
集合操作。Union ,minus ,intersect等,注:union all 是不排序的。
Order by
Group by
Distinct
In 有時候也會用到排序
確實要排序的時候也盡量要排序小數據量
,盡量讓排序在內存中執行,有文章說,內存排序的速度是硬盤排序的1萬倍。