索引與表類似,也可以分區;
分區索引分為兩類:
下面就來詳細解析一下這兩類索引。
一:Locally partitioned index(局部分區索引)
1. 概念:
局部分區索引隨表對索引完成相應的分區(即索引會使用與底層表相同的機制分區),每個表分區都有一個索引分區,並且只索引該表分區。
如圖,若一個表被劃分為AB兩個分區,則局部分區索引A就只索引A分區中的數據,局部分區索引B只索引B分區中的數據;
2. 分類:
局部分區索引又分為兩類:
Ⅰ:局部前綴索引:以分區鍵作為索引定義的第一列
Ⅱ:局部非前綴索引:分區鍵沒有作為索引定義的第一列
示例語句:
create table local_index_example ( id number(2), name varchar2(50), sex varchar2(10) ) partition by range (id) ( partition part_1 values less than (5), partition part_2 values less than (10) ) --創建局部前綴索引;分區鍵(id)作為索引定義的第一列 create index local_prefixed_index on local_index_example (id, name) local; --創建局部非前綴索引;分區鍵未作為索引定義的第一列 create index local_nonprefixed_index on local_index_example (name, id) local;
注意:判斷局部索引是前綴還是非前綴的只需要看分區鍵是否作為索引定義的第一列
3. 什麼時候該使用前綴索引?什麼時候該使用非前綴索引?
對於該使用前綴還是非前綴索引,這完全取決於你的實際需求,你應該盡量從實際角度出發選擇合適的索引方式以充分利用到其分區消除的特性。
如果查詢首先訪問索引的話,它能否實現分區消除完全取決於查詢中使用的謂詞(即Where篩選條件);
比如用上面的 local_index_example 表舉例,現有兩個查詢:
①: select … from local_index_example where id = :id and name = :name;
②: select … from local_index_example where name = :name;
對於以上兩個查詢來說,如果查詢第一步是走索引的話,則:
局部前綴索引 local_prefixed_index 只對 ① 有用;
局部非前綴索引 local_nonprefixed_index 則對 ① 和 ② 均有用;
如果你有多個類似 ① 和 ② 的查詢的話,則可以考慮建立局部非前綴索引;如果平常多使用查詢 ① 的話,則可以考慮建立局部前綴索引;
總之,重點是你要盡可能保證查詢包含的謂詞允許索引分區消除
-------------------延伸閱讀:綁定變量(bind variable)--------------------
綁定變量是查詢中的一個占位符,形如 :xxx 。
例如,要獲取 emp 表中 empno 為 123 的記錄,你可以執行如下兩種查詢:
①: select * from emp where empno = 123;
②: 先將綁定變量 :empno 的值設置為 123,再執行查詢 select * from emp where empno = :empno;
第一種查詢使用了 123 這樣一個直接量(常量),如果有多個這樣的查詢的話,則每一個查詢對數據庫來說都是一個全新的查詢,Oracle每次都會對查詢進行解析、限定(命名解析)、安全性檢查、優化等(簡單地講,就是每次執行時都要先編譯);
第二種查詢使用了 :empno 這樣一個綁定變量,變量值在查詢時動態指定,這個查詢只會在第一次時編譯,隨後Oracle會把查詢計劃存儲在一個共享池中方便以後重用,如此當以後再傳入不同的 empno 值進行查詢時,Oracle會直接調用第一次解析好的這個執行計劃進行執行,這樣查詢效率將大幅提升
------------------------------------------------------------------------
4. 局部索引的唯一性
Oracle只保證索引分區內部的唯一性,跨分區的唯一性無法保證。
如果你想使用局部索引實現唯一性約束的話,則必須讓分區鍵實現唯一性約束(UNIQUE 或 PRIMARY KEY)
二:Globally partitioned index(全局分區索引)
1. 概念:
全局分區索引,顧名思義,就是針對整個表空間(全局)來說的。
在此,索引按范圍(Range)或散列(Hash,Oracle 10g中引入)進行分區,一個分區索引(全局)可能指向任何(或全部的)表分區。
對於全局分區索引來說,索引的實際分區數可能不同於表的分區數量;
全局索引的分區機制有別於底層表,例如表可以按 done_date 列劃分為10個分區,表上的一個全局索引可以按 id 列劃分為5個分區。
與局部索引不同,全局索引只有一類,即全局前綴索引(prefixed global index),索引分區鍵必須作為索引定義的第一列,否則執行會報錯。
用例語句:
--創建示例表,按id進行范圍分區 create table global_index_example ( id number(2), name varchar2(50), age number(2) ) partition by range (id) ( partition part_1 values less than (5), partition part_2 values less than (10) ) --創建按age進行范圍分區的全局分區索引 create index global_index on global_index_example(age) global partition by range (age) ( partition index_part_1 values less than (20), partition index_part_2 values less than (maxvalue) )
注意:
全局索引要求最高分區(即最後一個分區)必須有一個值為 maxvalue 的最大上限值,這樣可以確保底層表的所有行都能放在這個索引中;
一般情況下,大多數分區操作(如刪除一個舊分區)都會使全局索引無效,除非重建全局索引,否則無法使用
2. 全局索引的使用:
1) 數據倉庫
許多數據倉庫系統都存在大量的數據出入,如典型的數據“滑入滑出”(即刪除表中最舊的分區,並為新加載的數據增加一個新分區);
這個過程涉及:
在 Oracle 9i 之前,對於創建的全局索引來說,這樣增刪分區的過程,意味著該全局索引的失效,你將不得不在最後花費相當長的時間重建全局索引;
在 Oracle 9i 之後,你可以在分區操作期間使用 UPDATE GLOBAL INEXES 子句來維護全局索引,這意味著當你在分區上執行刪除、分解或其他操作時,Oracle會對原先建立的全局索引執行必要的修改,以保證它是最新的
使用示例:
--刪除global_index_example表中的part_1分區,同時同步維護全局索引 alter table global_index_example drop partition part_1 update global indexes;
使用 UPDATE GLOBAL INEXES子句後,在刪除一個分區時,必須刪除可能指向該分區的所有全局索引條目;
執行表與分區的交換時,必須刪除指向原數據的所有全局索引條目,再插入指向剛加載的數據的新條目;
如此一來 ALTER 命令執行的工作量會大幅增加;
注意:使用 UPDATE GLOBAL INDEXES,將不能繞過 undo 或 redo 生成;
小結:
分區操作執行完成後重建全局索引方式占用的數據庫資源更少,因此完成的相對“更快”,但是會帶來顯著的“停機時間”(重建索引時會有一個可觀的不可用窗口);
在分區操作執行的同時執行 UPDATE GLOBAL INEXES 子句方式會占用更多的資源,且可能需要花費更長的時間才能完成操作,但好處是不會帶來任何的停機時間
----------------------------延伸閱讀:redo(重做信息) 與 undo(撤銷信息)------------------------------
什麼是redo?
redo log file(重做日志文件),是數據庫的事務日志。
Oracle維護著兩類重做日志文件:在線(online)重做日志文件和歸檔(archived)重做日志文件,這兩類重做日志文件用於實例失敗或是介質失敗時的數據恢復;
如果數據庫所在主機突然斷電導致實例失敗,則Oracle會使用在線重做日志將系統恰好恢復到掉電之前的時間點;
如果硬盤出現故障(即介質失敗),Oracle會使用歸檔重做日志和在線重做日志將硬盤上的數據恢復到適當的時間點;
另外如果你無意地刪除了某些重要信息並提交了這個操作,那麼可以恢復受影響數據的一個備份,並使用在線和歸檔重做日志文件把它恢復到之前的一個時間點;
重做日志可能是數據庫中最重要的恢復結構,但同時其他部分(如undo段、分布式事務恢復等)也不可或缺,重做日志是數據庫區別於傳統文件系統的一個主要特征;
什麼是undo?
當你對數數據執行修改(增、刪等)時,數據庫會生成undo信息,萬一你執行的事務或語句由於某些原因失敗時,或者你用一條 rollback 語句請求回滾時,數據庫就可以利用這些undo信息將數據返回到修改前的樣子。
redo用於在失敗時恢復事務,undo則用於取消一條語句或一組語句的作用;
undo信息存儲在數據庫內部一組特殊的段中(undo segment);
注意:
undo並不是使數據庫物理地恢復到執行語句或事務之前的樣子,數據庫只是邏輯地恢復到原來的樣子,所有修改都被邏輯地取消,但是數據結構以及數據庫塊在回滾後可能還與回滾前保持一致;
因為在多用戶系統中,可能會有數百或數千個並發事務,不僅僅你的事務在修改一些塊,其他許多人的事務可能也在修改這些塊;因此,不能簡單地將一個塊放回到你的事務開始前的樣子,這樣很可能會撤銷掉其他人的事務工作。
比如:
假設你的事務執行了一個 insert 語句,這條語句導致分配了一個新區段;
通過執行這個 insert,你將會獲得一個新的數據庫塊,並在格式化該塊後往其中放入一些數據;
此時,可能出現另外某個事務,它也往這個塊中插入數據;如果要回滾你的事務,顯然不能取消對這個數據庫塊已有的格式化和空間分配,否則會影響到另外的那個事務的工作。
因此在回滾時,Oracle實際上會做與先前邏輯上相反的工作,即:
對於每個 insert,會執行一個 delete;
對於每個 delete,會執行一個 insert;
對於每個 update,會執行一個“反update”,或者是執行另一個 update 將修改前的行放回去;
還有一點需要特別注意:undo生成對於直接路徑操作(即使用append提示進行insert)不適用,直接路徑操作能繞過表上的undo生成;
如此,redo與undo共同協作以保證數據的完整與安全性
--------------------------------------------------------------------------------------------------
2) OLTP系統
OLTP系統的特點是會頻繁出現許多小的讀寫事務,一般在OLTP系統中,首要的是需要快速訪問所需的行,其次數據的完整性、可用性也非常重要。
在OLTP系統中,很多情況下全局索引很有意義,比如當表按一列分區後,你可能還需要通過其他列來快速訪問數據,如此便可以考慮在這些列上建立全局索引。