2014-06-01 Created By BaoXinjian
一、摘要
在PLSQL查詢優化中,使用和接觸最多的應該是索引Index這個概念,個人也覺得對Index選擇和優化是程式優化過程中比較重要的概念,特別是剛開始接觸PLSQL性能優化
索引的一些概念
- 一個索引可以由一個或多個列組成,
- 對列設置索引其實就是對列的內容按一定的方式進行排序,檢索數據的時候,檢索排過序的數據,檢索到最后一個有效數據之后就跳出檢索
- 這樣就不必進行全表掃描了,同時可以應用很多算法提高檢索效率
- 數據庫多用二分法檢索數據
索引的連接方式
- Hash Join
- Nested Loops
- Merge Join
索引的分類
- B-樹索引
- 反向索引
- 降序索引
- 位圖索引
- 函數索引
- 建立索引后需要分析索引才能是索引生效
- 主鍵和唯一性索引的區別
索引的中Hints使用
- 強制索引
建立索引的代價
- 基礎表維護時,系統要同時維護索引,不合理的索引將嚴重影響系統資源,主要表現在CPU和I/O上;
- 插入、更新、刪除數據產生大量db file sequential read鎖等待;
有 些情況下雖然在查詢欄位中定義了index,但是PLSQL並沒有走該欄位的索引,因為Oracle在解析計划時一種基於Role,一種是基於Cost有 些情況下走Index的效率比全表掃秒更低,並不是建了Index,PLSQL性能會成倍提高,更糟的情況下,建Index還會導致性能比原來無索引更 糟,這里有個理解誤區,所以稍微提一下 ;)
而且,有時候你建立的索引可能應為空間或其他原因被失效后,所以會導致某些程式原本沒有任何問題,突然出現性能問題,而且是非常大性能問題,所以對EBS DBA也有較高的要求,能監控一些系統異常
二、索引的連接方式
1. Hash Join
2. Nested Loops
3. Merge Join
具體解析
1. Hash Join
(1).概述
i. 讀取一個表的資料,並將放置到內存中,並建立唯一關鍵字的位圖索引
ii. 讀取另一個表,和內存中表通過Hash算法進行比較
(2).適用對象
i. 大表連接小表
ii. 兩個大表
2. Nested Loops
(1).概述
i. 循環外表記錄
ii. 進行逐個比對和內標的連接是否符合條件
(2).適用對象
小表驅動大表,返回較少的結果集
3. Merge Join
(1).概述
i. 兩個表進行table access full
ii. 對table access full的結果進行排序
iii. 進行merge join對排序結構進行合並
(2).適用對象
通過rowid訪問數據
三、索引的分類
1. B-樹索引
2. 反向索引
3. 降序索引
4. 位圖索引
5. 函數索引
6. 建立索引后需要分析索引才能是索引生效
7. 主鍵和唯一性索引的區別
具體解析
1. B-樹索引
(1).概述
最常用的索引結構,默認建立的索引就是這種結構
適用於高基數數據列(該列的值大多不一樣)
(2).建立方式
CREATE INDEX index_name ON wip_entities (wip_entity);
2. 反向索引
(1).概述
(2).建立方式
3. 降序索引
(1).概述
適用於需要降序排列的列
(2).建立方式
CREATE INDEX index_name ON wip_entities (wip_entity DESC);
4. 位圖索引
(1).概述
適用於低基數數據列(該列的值大多是一樣)
(2).建立方式
CREATE BITMAP INDEX index_name ON wip_entities (sex);
5. 函數索引
(1).概述
適用於該列需要適用函數的列
(2).建立方式
CREATE INDEX index_name ON wip_entities (TRUNC(creation_date));
6. 分析索引
ANALYZE INDEX index_name COMPUTE STATISTICS;
7. 主鍵和唯一性索引的區別
(1). 主鍵是約束,唯一性索引只是一個索引
(2). 主鍵不可以為空,唯一性可以為空
四. 索引的中Hints使用 - 強制索引
1. 建立一個測試表,和測試索引
1 CREATE TABLE dba_name ( 2 3 username VARCHAR(100), 4 5 password VARCHAR(100) 6 7 ) ; 8 9 CREATE INDEX index_t ON dba_name(username) ;
2. 方法比較
(1). 未使用強制索引
SELECT *
FROM dba_name
WHERE username = 'BAOXINJIAN'
(2). 使用過強制索引
SELECT /*+ index(t index_t) */
*
FROM dba_name t
WHERE username = 'BAOXINJIAN'
(3). 有些情況下雖然在查詢欄位中定義了index,但是PLSQL並沒有走該欄位的索引,因為Oracle在解析計划時一種基於Role,一種是基於Cost
有些情況下走Index的效率比全表掃秒更低,並不是建了Index,PLSQL性能會成倍提高,更糟的情況下,建Index還會導致性能比原來無索引更糟,這里有個理解誤區,所以稍微提一下 ;)
五. 索引一些規則
1、權衡索引個數與DML之間關系,DML也就是插入、刪除數據操作。
這里需要權衡一個問題,建立索引的目的是為了提高查詢效率的,但建立的索引過多,會影響插入、刪除數據的速度,因為我們修改的表數據,索引也要跟着修改。這里需要權衡我們的操作是查詢多還是修改多。
2、把索引與對應的表放在不同的表空間。
當讀取一個表時表與索引是同時進行的。如果表與索引和在一個表空間里就會產生資源競爭,放在兩個表這空就可並行執行。
3、最好使用一樣大小是塊。
Oracle默認五塊,讀一次I/O,如果你定義6個塊或10個塊都需要讀取兩次I/O。最好是5的整數倍更能提高效率。
4、如果一個表很大,建立索引的時間很長,因為建立索引也會產生大量的redo信息,所以在創建索引時可以設置不產生或少產生redo信息。只要表數據存在,索引失敗了大不了再建,所以可以不需要產生redo信息。
5、建索引的時候應該根據具體的業務SQL來創建,特別是where條件,還有where條件的順序,盡量將過濾大范圍的放在后面,因為SQL執行是從后往前的
六. 索引的操作
改變索引:
SQL> alter index employees_last _name_idx storage(next 400K maxextents 100);索引創建后,感覺不合理,也可以對其參數進行修改。詳情查看相關文檔
2. 調整索引的空間:
2.1 新增加空間
SQL> alter index orders_region_id_idx allocate extent (size 200K datafile '/disk6/index01.dbf');
2.2 釋放空間
SQL> alter index oraers_id_idx deallocate unused;
索引在使用的過程中可能會出現空間不足或空間浪費的情況,這個時候需要新增或釋放空間。上面兩條命令完成新增與釋放操作。關於空間的新增oracle可以自動幫助,如果了解數據庫的情況下手動增加可以提高性能。
3. 重新創建索引:
所 引是由oracle自動完成,當我們對數據庫頻繁的操作時,索引也會跟着進行修改,當我們在數據庫中刪除一條記錄時,對應的索引中並沒有把相應的索引只是 做一個刪除標記,但它依然占據着空間。除非一個塊中所有的標記全被刪除的時,整個塊的空間才會被釋放。這樣時間久了,索引的性能就會下降。這個時候可以重 新建立一個干凈的索引來提高效率。
SQL> alter index orders_region_id_idx rebuild tablespace index02;通過上面的命令就可以重現建立一個索引,oracle重建立索引的過程:
1、鎖表,鎖表之后其他人就不能對表做任何操作。
2、創建新的(干凈的)臨時索引。
3、把老的索引刪除掉
4、把新的索引重新命名為老索引的名字
5、對表進行解鎖。
4. 移動所引:
其實,我們移動索引到其它表空間也同樣使用上面的命令,在指定表空間時指定不同的表空間。新的索引創建在別位置,把老的干掉,就相當於移動了。
SQL> alter index orders_region_id_idx rebuild tablespace index03;
5. 在線重新創建索引:
上面介紹,在創建索引的時候,表是被鎖定,不能被使用。對於一個大表,重新創建索引所需要的時間較長,為了滿足用戶對表操作的需求,就產生的這種在線重新創建索引。
SQL> alter index orders_id_idx rebuild online;創建過程:
1、鎖住表
2、創建立臨時的和空的索引和IOT表用來存在on-going DML。普通表存放的鍵值,IOT所引表直接存放的表中數據;on-gong DML也就是用戶所做的一些增刪改的操作。
3、對表進行解鎖
4、從老的索引創建一個新的索引。
5、IOT表里存放的是on-going DML信息,IOT表的內容與新創建的索引合並。
6、鎖住表
7、再次將IOT表的內容更新到新索引中,把老的索引干掉。
8、把新的索引重新命名為老索引的名字
9、對表進行解鎖
6. 整合索引碎片:
如上圖,在很多索引中有剩余的空間,可以通過一個命令把剩余空間整合到一起。
SQL> alter index orders_id_idx coalesce;
7. 刪除索引:
SQL> drop index hr.departments_name_idx;
七. 索引的分析
檢查所引的有效果,前面介紹,索引用的時間久了會產生大量的碎片、垃圾信息與浪費的剩余空間了。可以通過重新創建索引來提高所引的性能。
可以通過一條命令來完成分析索引,分析的結果會存放在在index_stats表中。
1. 查看存放分析數據的表:
SQL> select count(*) from index_stats;
COUNT(*)
----------
0
2. 執行分析索引命令:
SQL> analyze index my_bit_idx validate structure;
Index analyzed.
再次查看 index_stats 已經有了一條數據
SQL> select count(*) from index_stats;
COUNT(*)
----------
1
3. 把數據查詢出來:
SQL> select height,name,lf_rows,lf_blks,del_lf_rows from index_stats;
HEIGHT NAME LF_ROWS LF_BLKS DEL_LF_ROWS
---------- ---------------------------------------------------------------------- ---------- -----------
2 MY_BIT_IDX 1000 3 100 分析數據分析:
4. 分析
- (HEIGHT)這個所引高度是2 ;
- (NAME)索引名為MY_BIT_IDX ;
- (LF_ROWS)所引表有1000行數據;
- (LF_BLKS)占用3個塊;
- (DEL_LF_ROWS)刪除100條記錄’
- 這里也驗證了前面所說的一個問題,刪除的100條數據只是標記為刪除,因為總的數據條數依然為1000條,占用3個塊,那么每個塊大於333條記錄,只有刪除的數據大於333條記錄,這時一個塊被清空,總的數據條數才會減少。
八. 索引的優點缺點
1. 索引的優點
2. 索引的缺點
九. 未走索引的情況
以下是使用索引不當所引起的不走索引的幾種常見情況:
1. Index on t(x,y)但where 條件中只有y字段。
通常情況會進行全表掃描。
2. select count(*) from t通常由於索引比table小,oracle會進行index full scan。
但如果索引字段含有NULL值,則不會走索引,因為索引值不包含null,如果進行index full scan統計值就不准確了。
3. select * from t where f(index_column)=value
如果不是函數索引,where條件在索引字段上進行函數操作則不走索引
4. select * from t where indexed_column=5 字段類型需轉換。
例如indexed_column是字符但where條件中用了數字
5. oracle優化器認為全表掃描比走索引效率更高。
這種情況下oracle選擇全表掃描。如果開發人員覺得有必要走索引,可以使用hint強制走索引
6. 未及時對表進行analyze,statistics不准確。
例如原先是小表,后來數據量大增。由於statistics仍是舊的,oracle優化器會選擇不走索引
Thanks and Regards
