B*樹索引:這是Oracle中最常用的索引,它的構造類似于二叉樹,能根據(jù)鍵提供一行或一個行集的快速訪問,通常只需要很少的讀操作就能找到正確的行。B*樹索引由兩列組成,第一列是ROWID, 它是行的位置;第二列是正被索引列的值。
圖:典型的B*樹索引布局
這個樹底層的塊稱為葉子節(jié)點(leaf node) 或(leaf block),其中分別包含各個索引鍵以及一個rowid(它是指向所索引的行)。葉子節(jié)點之上的內(nèi)部塊稱為分支塊(branch block),這些節(jié)點用于實現(xiàn)導航。例如,如果想在索引中找到值20,要從樹頂開始,找到左分支,我們檢查這個塊,并發(fā)現(xiàn)需要找到范圍"20..25" 的塊,這個塊將是葉子塊,其中會指示包含數(shù)20的行。索引的葉子節(jié)點實際上構成了一個雙向鏈表。一旦發(fā)現(xiàn)要從葉子節(jié)點中的那里開始,執(zhí)行值的有序掃描 (index range scan)就會很容易,我們就不必再在索引結(jié)構中導航:而只需根據(jù)葉子節(jié)點向前或向后掃描就可以了。
B*樹的特點之一是:所有葉子塊都應該在樹的同一層上,這一層稱之為索引的高度, 它說明所有從索引的根塊到葉子塊的遍歷都會訪問同樣數(shù)目的塊。也就是說,對于形如"SELECT INDEX_column FROM TABLE WHERE INXDEX_column =:X"的索引,要達到葉子塊來獲取第一行,不論使用的:X值是什么,都會執(zhí)行同樣數(shù)目的I/O,由此可見B*樹的B代表的是balanced,所謂的"Height balanced"。大多數(shù)B*樹索引的高度都是2或3,即使索引中有數(shù)百萬行記錄也是如此,這說明,一般而言,在索引中找到一個鍵只需要2到3次I/O , 這確實不錯。
B*樹是一個極佳的通用索引機制,無論是大表還是小表都很適用,隨著底層表大小增長,獲取數(shù)據(jù)的性能僅會稍有惡化。
比如,我們?yōu)閏ustomers表建立一個常見的B*樹索引:
CREATE INDEX IDX_Cus_City on customers(city) |
B*樹索引有以下子類型:
復合索引
復合索引也是一種B*樹索引,它由多列組成。當我們擁有使用兩列或超過兩列的頻繁查詢時,就使用B*樹復合索引,而其所使用的兩列或多列在 where子句中and邏輯操作符連接。因為復合索引中列的順序很重要,所以確信以最有效的索引能排列他們,可以參考用作列排序的下面的兩個準則 :
1) 前導列應該是查詢中使用最頻繁的列。
2) 前導列應該是選擇最多的列,這意味著它比后面的列具有更高的基數(shù)。
復合索引在下列情況中具有優(yōu)勢:
1)假定在WHERE子句中頻繁使用下面的條件:order_status_id = 1 和order_date = ‘dd-mon-yyyy’。如果為每一列創(chuàng)建一個索引,那么為了搜索列的值,兩個索引都要被讀取,但是如果為兩列都創(chuàng)建一個復合索引,那么只有一個索引 被讀取,這樣無疑比兩個索引要求更少的I/O。
2) 使用前面例子中同樣的條件,如果創(chuàng)建一個復合索引,將更快地檢索行,因為你正在排除了所有order_status_id 不是1的行,從而減少了搜索order_date的行數(shù)。
反向鍵索引
B*樹索引的另一個特點是能夠?qū)⑺饕I“反轉(zhuǎn)”。首先,你可以問問自己“為什么想這么做?” B*樹索引是為特定的環(huán)境、特定的問題而設計的。實現(xiàn)B*樹索引的目的是為了減少“右側(cè)”索引中對索引葉子塊的競爭,比如在一個Oracle RAC 環(huán)境中,某些列用一個序列值或時間戳填充,這些列上建立的索引就屬于“右側(cè)”(right-hand-side)索引。
RAC 是一種Oracle 配置,其中多個實例可以裝載和打開同一個數(shù)據(jù)庫。如果兩個實例需要同時修改同一個數(shù)據(jù)塊,它們會通過一個硬件互連(interconnect)來回傳遞這 個塊來實現(xiàn)共享,互連是兩個(或多個)機器之間的一條專用網(wǎng)絡連接。如果某個利用一個序列填充,這個列上有一個主鍵索引 ,那么每個人插入新值時,都會視圖修改目前索引結(jié)構右側(cè)的左塊(見本文圖一,其中顯示出索引中較高的值都放在右側(cè),而較低的值放在左側(cè))。如果對用序列填 充的列上的索引進行修改,就會聚集在很少的一組葉子塊上。倘若將索引的鍵反轉(zhuǎn),對索引進行插入時,就能在索引中的所有葉子鍵上分布開(不過這往往會使索引 不能得到充分地填充)。
反向鍵索引創(chuàng)建語句語法如下:
CREATE INDEX index_name on table_name(column_name) REVERSE ; |
降序索引
降序索引(descending index)是oracle 8i引入的,用以擴展B*樹索引的功能,它允許在索引中以降序(從大到小的順序)存儲一列。在oracle8i及以上版本中,DESC關鍵字確實會改變創(chuàng)建和使用索引的的方式。
我們可以這樣創(chuàng)建降序索引
CREATE INDEX IDX_jobs_title on hr.jobs (job_title DESC); SET autotrace traceonly EXPLAIN; SELECT * FROM hr.jobs WHERE job_title Between 'a' AND 'ZZZZZZZZZZZ '; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=33) 1 0 FILTER 2 1 TABLE ACCESS (BY INDEX ROWID) OF 'JOBS' (Cost=1 Card=1 B ytes=33) 3 2 INDEX (RANGE SCAN) OF 'IDX_JOBS_TITLE' (NON-UNIQUE) (C ost=2 Card=1) SQL> SELECT * from hr.jobs 2 WHERE job_title between 'a' and 'ZZZZZZZZZZZ '; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=33) 1 0 FILTER 2 1 TABLE ACCESS (FULL) OF 'JOBS' (Cost=2 Card=1 Bytes=33) SQL> DROP INDEX IDX_jobs_title ; SQL> CREATE INDEX IDX_jobs_title on hr.jobs (job_title ); SQL> Select * FROM hr.jobs 2 Where job_title between 'a' and 'ZZZZZZZZZZZ '; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=33) 1 0 FILTER 2 1 TABLE ACCESS (FULL) OF 'JOBS' (Cost=2 Card=1 Bytes=33) |
位圖索引
位圖索引(bitmap index)是從Oracle7.3 版本開始引入的。目前Oracle企業(yè)版和個人版都支持位圖索引,但標準版不支持。位圖索引是為數(shù)據(jù)倉庫/在線分析查詢環(huán)境設計的,在此所有查詢要求的數(shù)據(jù)在系統(tǒng)實現(xiàn)時根本不知道。位圖索引特別不適用于OLTP 系統(tǒng),如果系統(tǒng)中的數(shù)據(jù)會由多個并發(fā)會話頻繁地更新,這種系統(tǒng)也不適用位圖索引。
位圖索引是這樣一種結(jié)構,其中用一個索引鍵條目存儲指向多行的指針;這與B*樹結(jié)構不同,在B*樹結(jié)構中,索引鍵和表中的行存在著對應關系。在位圖索引中,可能只有很少的索引條目,每個索引條目指向多行。而在傳統(tǒng)的B*樹中,一個索引條目就指向一行。
B*樹索引一般來講應當是選擇性的。與之相反,位圖索引不應是選擇性的,一般來講它們應該“沒有選擇性“。如果有大量在線分析查詢,特別是查詢 以一種即席方式引用了多列或者會生成諸如COUNT 之類的聚合,在這樣的環(huán)境中,位圖索引就特別有用 。位圖索引使用 CREATE BITMAP INDEX index_name ON table_name(column_name1,column_name2) TABLESPACE tablespace_name命令語法創(chuàng)建。
From: http://www.cnblogs.com/lovewindy/archive/2005/02/19/105959.html
在《數(shù)據(jù)庫原理》里面,對聚簇索引的解釋是:聚簇索引的順序就是數(shù)據(jù)的物理存儲順序,而對非聚簇索引的解釋是:索引順序與數(shù)據(jù)物理排列順序無關。正式因為如此,所以一個表最多只能有一個聚簇索引。
不過這個定義太抽象了。在SQL Server中,索引是通過二叉樹的數(shù)據(jù)結(jié)構來描述的,我們可以這么理解聚簇索引:索引的葉節(jié)點就是數(shù)據(jù)節(jié)點。而非聚簇索引的葉節(jié)點仍然是索引節(jié)點,只不過有一個指針指向?qū)臄?shù)據(jù)塊。如下圖:
非聚簇索引
聚簇索引
聚簇索引與非聚簇索引的本質(zhì)區(qū)別到底是什么?什么時候用聚簇索引,什么時候用非聚簇索引?
這是一個很復雜的問題,很難用三言兩語說清楚。我在這里從SQL Server索引優(yōu)化查詢的角度簡單談談(如果對這方面感興趣的話,可以讀一讀微軟出版的《Microsoft SQL Server 2000數(shù)據(jù)庫編程》第3單元的數(shù)據(jù)結(jié)構引論以及第6、13、14單元)。
一、索引塊與數(shù)據(jù)塊的區(qū)別
大家都知道,索引可以提高檢索效率,因為它的二叉樹結(jié)構以及占用空間小,所以訪問速度塊。讓我們來算一道數(shù)學題:如果表中的一條記錄在磁盤上占用1000字節(jié)的話,我們對其中10字節(jié)的一個字段建立索引,那么該記錄對應的索引塊的大小只有10字節(jié)。我們知道,SQL Server的最小空間分配單元是“頁(Page)”,一個頁在磁盤上占用8K空間,那么這一個頁可以存儲上述記錄8條,但可以存儲索引800條?,F(xiàn)在我們要從一個有8000條記錄的表中檢索符合某個條件的記錄,如果沒有索引的話,我們可能需要遍歷8000條×1000字節(jié)/8K字節(jié)=1000個頁面才能夠找到結(jié)果。如果在檢索字段上有上述索引的話,那么我們可以在8000條×10字節(jié)/8K字節(jié)=10個頁面中就檢索到滿足條件的索引塊,然后根據(jù)索引塊上的指針逐一找到結(jié)果數(shù)據(jù)塊,這樣IO訪問量要少的多。
二、索引優(yōu)化技術
是不是有索引就一定檢索的快呢?答案是否。有些時候用索引還不如不用索引快。比如說我們要檢索上述表中的所有記錄,如果不用索引,需要訪問8000條×1000字節(jié)/8K字節(jié)=1000個頁面,如果使用索引的話,首先檢索索引,訪問8000條×10字節(jié)/8K字節(jié)=10個頁面得到索引檢索結(jié)果,再根據(jù)索引檢索結(jié)果去對應數(shù)據(jù)頁面,由于是檢索所有數(shù)據(jù),所以需要再訪問8000條×1000字節(jié)/8K字節(jié)=1000個頁面將全部數(shù)據(jù)讀取出來,一共訪問了1010個頁面,這顯然不如不用索引快。
SQL Server內(nèi)部有一套完整的數(shù)據(jù)檢索優(yōu)化技術,在上述情況下,SQL Server的查詢計劃(Search Plan)會自動使用表掃描的方式檢索數(shù)據(jù)而不會使用任何索引。那么SQL Server是怎么知道什么時候用索引,什么時候不用索引的呢?SQL Server除了日常維護數(shù)據(jù)信息外,還維護著數(shù)據(jù)統(tǒng)計信息,下圖是數(shù)據(jù)庫屬性頁面的一個截圖:
從圖中我們可以看到,SQL Server自動維護統(tǒng)計信息,這些統(tǒng)計信息包括數(shù)據(jù)密度信息以及數(shù)據(jù)分布信息,這些信息幫助SQL Server決定如何制定查詢計劃以及查詢是是否使用索引以及使用什么樣的索引(這里就不再解釋它們到底如何幫助SQL Server建立查詢計劃的了)。我們還是來做個實驗。建立一張表:tabTest(ID, unqValue,intValue),其中ID是整形自動編號主索引,unqValue是uniqueidentifier類型,在上面建立普通索引,intValue 是整形,不建立索引。之所以掛上一個沒有索引的intValue字段,就是防止SQL Server使用索引覆蓋查詢優(yōu)化技術,這樣實驗就起不到作用了。向表中錄入10000條隨機記錄,代碼如下:
CREATE TABLE [dbo].[tabTest] (
[ID] [int] IDENTITY (1, 1) NOT NULL ,
[unqValue] [uniqueidentifier] NOT NULL ,
[intValue] [int] NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[tabTest] WITH NOCHECK ADD
CONSTRAINT [PK_tabTest] PRIMARY KEY CLUSTERED
(
[ID]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[tabTest] ADD
CONSTRAINT [DF_tabTest_unqValue] DEFAULT (newid()) FOR [unqValue]
GO
CREATE INDEX [IX_tabTest_unqValue] ON [dbo].[tabTest]([unqValue]) ON [PRIMARY]
GO
declare @i int
declare @v int
set @i=0
while @i<10000
begin
set @v=rand()*1000
insert into tabTest ([intValue]) values (@v)
set @i=@i+1
end
然后我們執(zhí)行兩個查詢并查看執(zhí)行計劃,如圖:(在查詢分析器的查詢菜單中可以打開查詢計劃,同時圖上第一個查詢的GUID是我從數(shù)據(jù)庫中找的,大家做實驗的時候可以根據(jù)自己數(shù)據(jù)庫中的值來定):
從圖中可以看出,在第一個查詢中,SQL Server使用了IX_tabTest_unqValue索引,根據(jù)箭頭方向,計算機先在索引范圍內(nèi)找,找到后,使用Bookmark Lookup將索引節(jié)點映射到數(shù)據(jù)節(jié)點上,最后給出SELECT結(jié)果。在第二個查詢中,系統(tǒng)直接遍歷表給出結(jié)果,不過它使用了聚簇索引,為什么呢?不要忘了,聚簇索引的頁節(jié)點就是數(shù)據(jù)節(jié)點!這樣使用聚簇索引會更快一些(不受數(shù)據(jù)刪除、更新留下的存儲空洞的影響,直接遍歷數(shù)據(jù)是要跳過這些空洞的)。
下面,我們在SQL Server中將ID字段的聚簇索引更改為非聚簇索引,然后再執(zhí)行select * from tabTest,這回我們看到的執(zhí)行計劃變成了:
SQL Server沒有使用任何索引,而是直接執(zhí)行了Table Scan,因為只有這樣,檢索效率才是最高的。
三、聚簇索引與非聚簇索引的本質(zhì)區(qū)別
現(xiàn)在可以討論聚簇索引與非聚簇索引的本質(zhì)區(qū)別了。正如本文最前面的兩個圖所示,聚簇索引的葉節(jié)點就是數(shù)據(jù)節(jié)點,而非聚簇索引的頁節(jié)點仍然是索引檢點,并保留一個鏈接指向?qū)獢?shù)據(jù)塊。
還是通過一道數(shù)學題來看看它們的區(qū)別吧:假設有一8000條記錄的表,表中每條記錄在磁盤上占用1000字節(jié),如果在一個10字節(jié)長的字段上建立非聚簇索引主鍵,需要二叉樹節(jié)點16000個(這16000個節(jié)點中有8000個葉節(jié)點,每個頁節(jié)點都指向一個數(shù)據(jù)記錄),這樣數(shù)據(jù)將占用8000條×1000字節(jié)/8K字節(jié)=1000個頁面;索引將占用16000個節(jié)點×10字節(jié)/8K字節(jié)=20個頁面,共計1020個頁面。
同樣一張表,如果我們在對應字段上建立聚簇索引主鍵,由于聚簇索引的頁節(jié)點就是數(shù)據(jù)節(jié)點,所以索引節(jié)點僅有8000個,占用10個頁面,數(shù)據(jù)仍然占有1000個頁面。
下面我們看看在執(zhí)行插入操作時,非聚簇索引的主鍵為什么比聚簇索引主鍵要快。主鍵約束要求主鍵不能出現(xiàn)重復,那么SQL Server是怎么知道不出現(xiàn)重復的呢?唯一的方法就是檢索。對于非聚簇索引,只需要檢索20個頁面中的16000個節(jié)點就知道是否有重復,因為所有主鍵鍵值在這16000個索引節(jié)點中都包含了。但對于聚簇索引,索引節(jié)點僅僅包含了8000個中間節(jié)點,至于會不會出現(xiàn)重復必須檢索另外1000個頁數(shù)據(jù)節(jié)點才知道,那么相當于檢索10+1000=1010個頁面才知道是否有重復。所以聚簇索引主鍵的插入速度要比非聚簇索引主鍵的插入速度慢很多。
讓我們再來看看數(shù)據(jù)檢索的效率,如果對上述兩表進行檢索,在使用索引的情況下(有些時候SQL Server執(zhí)行計劃會選擇不使用索引,不過我們這里姑且假設一定使用索引),對于聚簇索引檢索,我們可能會訪問10個索引頁面外加1000個數(shù)據(jù)頁面得到結(jié)果(實際情況要比這個好),而對于非聚簇索引,系統(tǒng)會從20個頁面中找到符合條件的節(jié)點,再映射到1000個數(shù)據(jù)頁面上(這也是最糟糕的情況),比較一下,一個訪問了1010個頁面而另一個訪問了1020個頁面,可見檢索效率差異并不是很大。所以不管非聚簇索引也好還是聚簇索引也好,都適合排序,聚簇索引僅僅比非聚簇索引快一點。
結(jié)語
好了,寫了半天,手都累了。關于聚簇索引與非聚簇索引效率問題的實驗就不做了,感興趣的話可以自己使用查詢分析器對查詢計劃進行分析。SQL Server是一個很復雜的系統(tǒng),尤其是索引以及查詢優(yōu)化技術,Oracle就更復雜了。了解索引以及查詢背后的事情不是什么壞事,它可以幫助我們更為深刻的了解我們的系統(tǒng)。
設計和實現(xiàn)視圖可謂是數(shù)據(jù)庫物理設計中的一個非常重要的步驟。從一般意義上說,設計和實現(xiàn)視圖應該遵循下面的一些建議和原則。
以下內(nèi)容摘在文檔,我對某些重點進行了補充說明(紅色部分)
只能在當前數(shù)據(jù)庫中創(chuàng)建視圖。 但是,如果使用分布式查詢定義視圖,則新視圖所引用的表和視圖可以存在于其他數(shù)據(jù)庫甚至其他服務器中。
- 分布式視圖是可行的,但隨著SQL Server本身能力的提高,例如SQL Server 2005開始支持表分區(qū)等技術之后,分布式視圖應該盡量少用。
- 所謂分布式視圖的一個最大的問題就是將表物理上分開在多個數(shù)據(jù)庫甚至服務器中,這增加了維護和查詢的難度
視圖名稱必須遵循標識符的規(guī)則,且對每個架構都必須唯一。 此外,該名稱不得與該架構包含的任何表的名稱相同。
- 一個可以借鑒的做法是:在視圖名稱之前添加一個前綴 vw
您可以對其他視圖創(chuàng)建視圖。Microsoft SQL Server 允許嵌套視圖。但嵌套不得超過 32 層。 根據(jù)視圖的復雜性及可用內(nèi)存,視圖嵌套的實際限制可能低于該值。
不能將規(guī)則或 DEFAULT 定義與視圖相關聯(lián)。不能將 AFTER 觸發(fā)器與視圖相關聯(lián),只有 INSTEAD OF 觸發(fā)器可以與之相關聯(lián)。
定義視圖的查詢不能包含 COMPUTE 子句、COMPUTE BY 子句或 INTO 關鍵字。
- 很多朋友不知道:COMPUTER和COMPUTER BY語句僅僅用于一些特殊場合,用于生成總計行。大致有如下的效果
該特性不能用于視圖,但可以直接用于查詢
定義視圖的查詢不能包含 ORDER BY 子句,除非在 SELECT 語句的選擇列表中還有一個 TOP 子句。
- 這個很有意思,如果要訪問所有的呢,還必須是寫TOP 100 PERCENT
定義視圖的查詢不能包含指定查詢提示的 OPTION 子句。定義視圖的查詢不能包含 TABLESAMPLE 子句。
不能為視圖定義全文索引定義。不能創(chuàng)建臨時視圖,也不能對臨時表創(chuàng)建視圖。
- 在SQL Server 2005中,可以通過CTE(Common Table Expression)來實現(xiàn)該功能
- 之前的版本,大致的做法是使用臨時表,表變量,函數(shù)等等
不能刪除參與到使用 SCHEMABINDING 子句創(chuàng)建的視圖中的視圖、表或函數(shù),除非該視圖已被刪除或更改而不再具有架構綁定。 另外,如果對參與具有架構綁定的視圖的表執(zhí)行 ALTER TABLE 語句,而這些語句又會影響該視圖的定義,則這些語句將會失敗。
- 如果未使用 SCHEMABINDING 子句創(chuàng)建視圖,則對視圖下影響視圖定義的對象進行更改時,應運行 sp_refreshview。 否則,當查詢視圖時,可能會生成意外結(jié)果。
- 如果你修改了一個表,那么如何刷新所有與該表有關的視圖呢
- 強烈建議對某些非常重要的視圖,添加SCHEMABINDING 子句。
盡管查詢引用一個已配置全文索引的表時,視圖定義可以包含全文查詢,仍然不能對視圖執(zhí)行全文查詢。下列情況下必須指定視圖中每列的名稱:
- 視圖中的任何列都是從算術表達式、內(nèi)置函數(shù)或常量派生而來。
- 視圖中有兩列或多列原應具有相同名稱(通常由于視圖定義包含聯(lián)接,因此來自兩個或多個不同表的列具有相同的名稱)。
- 希望為視圖中的列指定一個與其源列不同的名稱。 (也可以在視圖中重命名列。) 無論重命名與否,視圖列都會繼承其源列的數(shù)據(jù)類型。
若要創(chuàng)建視圖,您必須獲取由數(shù)據(jù)庫所有者授予的此操作執(zhí)行權限,如果使用 SCHEMABINDING 子句創(chuàng)建視圖,則必須對視圖定義中引用的任何表或視圖具有相應的權限。
默認情況下,由于行通過視圖進行添加或更新,當其不再符合定義視圖的查詢的條件時,它們即從視圖范圍中消失。 例如,創(chuàng)建一個定義視圖的查詢,該視圖從表中檢索員工的薪水低于 $30,000 的所有行。如果員工的薪水漲到 $32,000,因其薪水不符合視圖所設條件,查詢時視圖不再顯示該特定員工。 但是,WITH CHECK OPTION 子句強制所有數(shù)據(jù)修改語句均根據(jù)視圖執(zhí)行,以符合定義視圖的 SELECT 語句中所設條件。 如果使用該子句,則對行的修改不能導致行從視圖中消失。 任何可能導致行消失的修改都會被取消,并顯示錯誤。