索引的基本原理,以及數據是如何被訪問的
(一)SQLS如何訪問沒有建立索引的數據表
Heap譯成漢語叫做“堆”,其本義暗含雜亂無章、無序的意思,前面提到數據值被寫進數據頁時,由于每一行記錄之間并沒有特定的排列順序,所以行與行的順序就是隨機無序的,當然表中的數據頁也就是無序的了,而表中所有數據頁就形成了“堆”。可以說,一張沒有索引的數據表,就像一個只有書柜而沒有索引卡片柜的圖書館,書庫里面塞滿了一堆亂七八糟的圖書。當讀者對管理員提交查詢請求后,管理員就一頭鉆進書庫,對照查找內容從頭開始一架一柜的逐本查找。運氣好的話,在第一個書架的第一本書就 找到了,運氣不好的話,要到最后一個書架的最后一本書才找到。
SQLS在接到查詢請求時,首先會分析sysindexes表中一個叫做索引標志符(INDID:?Index?ID)的字段的值,如果該值為0,表示這是一張數據表而不是索引表,SQLS就會使用sysindexes表的另一個字段——也就是在前面提到過的FirstIAM值中找到該表的IAM頁鏈,也就是所有數據頁集合。
這就是對一個沒有建立索引的數據表進行數據查找的方式,是不是很沒效率?對于沒有索引的表,對于一“堆”這樣的記錄,SQLS也只能這樣做,而且更沒勁的是,即使在第一行就找到了被查詢的記錄,SQLS仍然要從頭到尾的將表掃描一次。這種查詢稱為“遍歷”,又叫“表掃描”。
可見沒有建立索引的數據表照樣可以運行,不過這種方法對于小規模的表來說沒有什么太大的問題,但要查詢海量的數據效率就太低了。
(二)SQLS如何訪問建立了非聚集索引的數據表
如前所述,非聚集索引可以建多個,具有B樹結構,其葉級節點不包含數據頁,只包含索引行。假定一個表中只有非聚集索引,則每個索引行包含了非聚集索引鍵值以及行定位符(ROW?ID,RID),他們指向具有該鍵值的數據行,每一個RID由文件ID、頁編號和在頁中行的編號組成。
當INDID的值在2至250之間時,意味著表中存在非聚集索引頁。此時,SQLS調用ROOT字段的值指向非聚集索引B樹的ROOT,在其中查找與被查詢最相近的值,根據這個值找到在非葉級節點中的頁號,然后順藤摸瓜,在葉級節點相應的頁面中找到該值的RID,最后根據這個RID在Heap中定位所在的頁和行并返回到查詢端。
例如:假定在Lastname上建立了非聚集索引,則執行Select?*?From?Member?Where?Lastname=’Ota’時,查詢過程是:
①SQLS查詢INDID值為2;
②立即從根出發,在非葉級節點中定位最接近Ota的值“Martin”,并查到其位于葉級頁面的第61頁;
③僅在葉級頁面的第61頁的Martin下搜尋Ota的RID,其RID顯示為N∶706∶4,表示Lastname字段中名 為Ota的記錄位于堆的第706頁的第4行,N表示文件的ID值,與數據無關;
④根據上述信息,SQLS立刻在堆的第706頁第4行將該記錄“揪”出來并顯示于前臺(客戶端)。視表的數據量大小,整個查詢過程費時從百分之幾毫秒到數毫秒不等。
在談到索引基本概念的時候,我們就提到了這種方式:圖書館的前臺有很多索引卡片柜,里面分了若干的類別,諸如按照書名筆畫或拼音順序、作者筆畫或拼音順序等,但有兩點不同之處:
①?索引卡片上記錄了每本書擺放的具體位置——位于某柜某架的第幾本——而不是“特殊編號”;
②?書脊上并沒有那個“特殊編號”。管理員在索引柜中查到所需圖書的具體位置(RID)后,根據RID直接在書庫中的具體位置將書提出來。
顯然,這種查詢方式效率很高,但資源占用極大,因為書庫中書的位置隨時在發生變化,必然要求管理員花費額外的精力和時間隨時做好索引更新。
(三)SQLS如何訪問建立聚集索引的數據表
在聚集索引中,數據所在的數據頁是葉級,索引數據所在的索引頁是非葉級。
查詢原理和上述對非聚集索引的查詢相似,但由于記錄是按照聚集索引中索引鍵值進行排序,換句話說,聚集索引的索引鍵值也就是具體的數據頁。
這就好比書庫中的書就是按照書名的拼音在排序,而且也只按照這一種排序方式建立相應的索引卡片,于是查詢起來要比上述只建立非聚集索引的方式要簡單得多。仍以上面的查詢為例:
假定在Lastname字段上建立了聚集索引,則執行Select?*?From?Member?Where?Lastname=’Ota’時,查詢過程是:
①SQLS查詢INDID值為1,這是在系統中只建立了聚集索引的標志;
②立即從根出發,在非葉級節點中定位最接近Ota的值“Martin”,并查到其位于葉級頁面的第120頁;
③在位于葉級頁面第120頁的Martin下搜尋到Ota條目,而這一條目已是數據記錄本身;
④將該記錄返回客戶端。
這一次的效率比第二種方法更高,以致于看起來更美,然而它最大的優點也恰好是它最大的缺點——由于同一張表中同時只能按照一種順序排列,所以在任何一種數據表中的聚集索引只能建立一個;并且建立聚集索引需要至少相當于源表120%的附加空間,以存放源表的副本和索引中間頁。
難道魚和熊掌就不能兼顧了嗎?辦法是有的。
(四)SQLS如何訪問既有聚集索引、又有非聚集索引的數據表
如果我們在建立非聚集索引之前先建立了聚集索引的話,那么非聚集索引就可以使用聚集索引的關鍵字進行檢索。就像在圖書館中,前臺卡片柜中可以有不同類別的圖書索引卡,然而每張卡片上都載明了那個特殊編號——并不是書籍存放的具體位置。這樣在最大程度上既照顧了數據檢索的快捷性,又使索引的日常維護變得更加可行,這是最為科學的檢索方法。
也就是說,在只建立了非聚集索引的情況下,每個葉級節點指明了記錄的行定位符(RID);而在既有聚集索引又有非聚集索引的情況下,每個葉級節點所指向的是該聚集索引的索引鍵值,即數據記錄本身。
假設聚集索引建立在Lastname上,而非聚集索引建立在Firstname上,當執行Select?*?From?Member?Where?Firstname=’Mike’時,查詢過程是:
①SQLS查詢INDID值為2;
②立即從根出發,在Firstname的非聚集索引的非葉級節點中定位最接近Mike的值“Jose”條目;
③從Jose條目下的葉級頁面中查到Mike邏輯位置——不是RID而是聚集索引的指針;
④根據這一指針所指示位置,直接進入位于Lastname的聚集索引中的葉級頁面中到達Mike數據記錄本身;
⑤將該記錄返回客戶端。
這就完全和我們在“索引的基本概念”中講到的現實場景完全一樣了,當數據發生更新的時候,SQLS只負責對聚集索引的鍵值加以維護,而不必考慮非聚集索引。只要我們在ID類的字段上建立聚集索引,而在其它經常需要查詢的字段上建立非聚集索引,通過這種科學的、有針對性的在一張表上分別建立聚集索引和非聚集索引的方法,我們既享受了索引帶來的靈活與快捷,又相對避免了維護索引所導致的大量的額外資源消耗。
索引的優點和不足
索引有一些先天不足
1、系統要占用大約為表的1.2倍的硬盤和內存空間來保存索引;
2、更新數據的時候,系統必須要有額外的時間來同時對索引進行更新,以維持數據和索引的一致性。
當然建立索引的優點也是顯而易見的,在海量數據的情況下,如果合理的建立了索引,則會大大加強SQLS執行查詢、對結果進行排序、分組的操作效率。
實踐表明,不恰當的索引不但于事無補,反而會降低系統性能。因為大量的索引在進行插入、修改和刪除操作時比沒有索引要花費更多的系統時間。
在如下字段建立索引應該是不恰當的:
1、很少或從不引用的字段;
2、邏輯型的字段,如男或女(是或否)等。
綜上所述,提高查詢效率是以消耗一定的系統資源為代價的,索引不能盲目的建立,必須要有統籌的規劃,一定要在“加快查詢速度”與“降低修改速度”之間做好平衡。有得必有失,此消則彼長,這是考驗一個DBA是否優秀的很重要的指標
建立索引時一定要在“加快查詢速度”與“降低修改速度”之間做好平衡,有得必有失,此消則彼長。那么,SQLS維護索引時究竟怎樣消耗資源?應該從哪些方面對索引進行管理與優化?以下從六個方面來回答這些問題。
一.頁分裂
微軟MOC教導我們:當一個數據頁達到了8K容量,如果此時發生插入或更新數據的操作,將導致頁的分裂(又名頁拆分):
1.有聚集索引的情況下:聚集索引將被插入和更新的行指向特定的頁,該頁由聚集索引關鍵字決定;
2.只有堆的情況下:只要有空間就可以插入新的行,但是如果我們對行數據的更新需要更多的空間,以致大于當前頁的可用空間,行就被移到新的頁中,并且在原位置留下一個轉發指針,指向被移動的新行,如果具有轉發指針的行又被移動了,那么原來的指針將重新指向新的位置;
3.如果堆中有非聚集索引,那么盡管插入和更新操作在堆中不會發生頁分裂,但是在非聚集索引上仍然產生頁分裂。
無論有無索引,大約一半的數據將保留在老頁面,而另一半將放入新頁面,并且新頁面可能被分配到任何可用的頁。所以,頻繁頁分裂,后果很嚴重,將使物理表產生大量數據碎片,導致直接造成I/O效率的急劇下降,最后,不得不停止SQLS的運行并重建索引。
二.填充因子
然而在“混沌之初”,就可以在一定程度上避免不愉快出現,在創建索引時,可以為這個索引指定一個填充因子,以便在索引的每個葉級頁面上保留一定百分比的空間,將來數據可以進行擴充和減少頁分裂。填充因子是從0到100的百分比數值,設為100時表示將數據頁填滿,只有當不會對數據進行更改時(例如只讀表中)才用此設置。值越小則數據頁上的空閑空間越大,這樣可以減少在索引增長過程中進行頁分裂的需要,但這一操作需要占用更多的硬盤空間。
填充因子只在創建索引時執行,索引創建以后,當表中進行數據的添加、刪除或更新時,是不會保持填充因子的,如果想在數據頁上保持額外的空間,則有悖于使用填充因子的本意,因為隨著數據的輸入,SQLS必須在每個頁上進行頁拆分,以保持填充因子指定的空閑空間。因此,只有在表中的數據進行了較大的變動,才可以填充數據頁的空閑空間。這時,可以從容的重建索引,重新指定填充因子,重新分布數據。
反之,填充因子指定不當,就會降低數據庫的讀取性能,其降低量與填充因子設置值成反比。例如,當填充因子的值為50時,數據庫的讀取性能會降低兩倍。所以,只有在表中根據現有數據創建新索引,并且可以預見將來會對這些數據進行哪些更改時,設置填充因子才有意義。
三.兩道數學題
假定數據庫設計沒有問題,那么是否像上篇分析的那樣,當你建立了眾多的索引,在查詢工作中SQLS就只能按照“最高指示”用索引處理每一個提交的查詢呢?答案是否定的。
實際上,SQLS幾乎完全是“自主”的決定是否使用索引或使用哪一個索引。
這是怎么回事呢?
讓我們先來算一道題:如果某表的一條記錄在磁盤上占用1000字節(1K)的話,我們對其中10字節的一個字段建立索引,那么該記錄對應的索引大小只有10字節(0.01K)。上篇說過,SQLS的最小空間分配單元是“頁(Page)”,一個頁面在磁盤上占用8K空間,所以一頁只能存儲8條“記錄”,但可以存儲800條“索引”。現在我們要從一個有8000條記錄的表中檢索符合某個條件的記錄(有Where子句),如果沒有索引的話,我們需要遍歷8000條×1000字節/8K字節=1000個頁面才能夠找到結果。如果在檢索字段上有上述索引的話,那么我們可以在8000條×10字節/8K字節=10個頁面中就檢索到滿足條件的索引塊,然后根據索引塊上的指針逐一找到結果數據塊,這樣I/O訪問量肯定要少得多。
然而有時用索引比不用索引還快。
同上,如果要無條件檢索全部記錄(不用Where子句),不用索引的話,需要訪問8000條×1000字節/8K字節=1000個頁面;而使用索引的話,首先檢索索引,訪問8000條×10字節/8K字節=10個頁面得到索引檢索結果,再根據索引檢索結果去對應數據頁面,由于是檢索全部數據,所以需要再訪問8000條×1000字節/8K字節=1000個頁面將全部數據讀取出來,一共訪問了1010個頁面,這顯然不如不用索引快。
SQLS內部有一套完整的數據索引優化技術,在上述情況下,SQLS會自動使用表掃描的方式檢索數據而不會使用任何索引。那么SQLS是怎么知道什么時候用索引,什么時候不用索引的呢?因為SQLS除了維護數據信息外,還維護著數據統計信息。
四.統計信息
打開企業管理器,單擊“Database”節點,右擊Northwind數據庫→單擊“屬性”→選擇“Options”選項卡,觀察“Settings”下的各項復選項,你發現了什么?
從Settings中我們可以看到,在數據庫中,SQLS將默認的自動創建和更新統計信息,這些統計信息包括數據密度和分布信息,正是它們幫助SQLS確定最佳的查詢策略:建立查詢計劃和是否使用索引以及使用什么樣的索引。
在創建索引時,SQLS會創建分布數據頁來存放有關索引的兩種統計信息:分布表和密度表。查詢優化器使用這些統計信息估算使用該索引進行查詢的成本(Cost),并在此基礎上判斷該索引對某個特定查詢是否有用。
隨著表中的數據發生變化,SQLS自動定期更新這些統計信息。采樣是在各個數據頁上隨機進行。從磁盤讀取一個數據頁后,該數據頁上的所有行都被用來更新統計信息。統計信息更新的頻率取決于字段或索引中的數據量以及數據更改量。比如,對于有一萬條記錄的表,當1000個索引鍵值發生改變時,該表的統計信息便可能需要更新,因為1000?個值在該表中占了10%,這是一個很大的比例。而對于有1千萬條記錄的表來說,1000個索引值發生更改的意義則可以忽略不計,因此統計信息就不會自動更新。
五.索引的人工維護
上面講到,某些不合適的索引將影響到SQLS的性能,隨著應用系統的運行,數據不斷地發生變化,當數據變化達到某一個程度時將會影響到索引的使用。這時需要用戶自己來維護索引。
隨著數據行的插入、刪除和數據頁的分裂,有些索引頁可能只包含幾頁數據,另外應用在執行大量I/O的時候,重建非聚聚集索引可以維護I/O的效率。重建索引實質上是重新組織B樹。需要重建索引的情況有:
1.數據和使用模式大幅度變化;
2.排序的順序發生改變;
3.要進行大量插入操作或已經完成;
4.使用I/O查詢的磁盤讀次數比預料的要多;
5.由于大量數據修改,使得數據頁和索引頁沒有充分使用而導致空間的使用超出估算;
6.dbcc檢查出索引有問題。
六.索引的使用原則
接近尾聲的時候,讓我們再從另一個角度認識索引的兩個重要屬性----惟一性索引和復合性索引。
惟一性索引保證在索引列中的全部數據是惟一的,不會包含冗余數據。如果表中已經有一個主鍵約束或者惟一性約束,那么當創建表或者修改表時,SQLS自動創建一個惟一性索引。但出于必須保證惟一性,那么應該創建主鍵約束或者惟一性鍵約束,而不是創建一個惟一性索引。
復合索引就是一個索引創建在兩個列或者多個列上。在搜索時,當兩個或者多個列作為一個關鍵值時,最好在這些列上創建復合索引。當創建復合索引時,應該考慮這些規則:最多可以把16個列合并成一個單獨的復合索引,構成復合索引的列的總長度不能超過900字節;在復合索引中,所有的列必須來自同一個表中,不能跨表建立復合列;在復合索引中,列的排列順序是非常重要的,原則上,應該首先定義最惟一的列,例如在(COL1,COL2)上的索引與在(COL2,COL1)上的索引是不相同的,因為兩個索引的列的順序不同;為了使查詢優化器使用復合索引,查詢語句中的WHERE子句必須參考復合索引中第一個列。
綜上所述,我們總結了如下索引使用原則:
1.邏輯主鍵使用惟一的成組索引,對系統鍵(作為存儲過程)采用惟一的非成組索引,對任何外鍵列采用非成組索引。考慮數據庫的空間有多大,表如何進行訪問,還有這些訪問是否主要用作讀寫;
2.不要索引memo/note?字段,不要索引大型字段(有很多字符),這樣作會讓索引占用太多的存儲空間;
3.不要索引常用的小型表;
4.一般不要為小型數據表設置過多的索引,如果經常有插入和刪除操作就更不要設置索引,因為SQLS對插入和刪除操作提供的索引維護可能比掃描表空間消耗的時間更多。
查詢是一個物理過程,表面上是SQLS在東跑西跑,其實真正大部分壓馬路的工作是由磁盤輸入輸出系統(I/O)完成,全表掃描需要從磁盤上讀表的每一個數據頁,如果有索引指向數據值,則I/O讀幾次磁盤就可以了。但是,在隨時發生的增、刪、改操作中,索引的存在會大大增加工作量,因此,合理的索引設計是建立在對各種查詢的分析和預測上的,只有正確地使索引與程序結合起來,才能產生最佳的優化方案。
SQLS是一個很復雜的系統,讓索引以及查詢背后的東西真相大白,可以幫助我們更為深刻的了解我們的系統。一句話,索引就像鹽,少則無味多則咸。
DBCC?DBREINDEX重建索引提高SQL?Server性能
大多數SQL?Server表需要索引來提高數據的訪問速度,如果沒有索引,SQL?Server?要進行表格掃描讀取表中的每一個記錄才能找到索要的數據。索引可以分為簇索引和非簇索引,簇索引通過重排表中的數據來提高數據的訪問速度,而非簇索引則通過維護表中的數據指針來提高數據的索引。
1.?索引的體系結構
為什么要不斷的維護表的索引?首先,簡單介紹一下索引的體系結構。SQL?Server在硬盤中用8KB頁面在數據庫文件內存放數據。缺省情況下這些頁面及其包含的數據是無組織的。為了使混亂變為有序,就要生成索引。生成索引后,就有了索引頁和數據頁,數據頁保存用戶寫入的數據信息。索引頁存放用于檢索列的數據值清單(關鍵字)和索引表中該值所在紀錄的地址指針。索引分為簇索引和非簇索引,簇索引實質上是將表中的數據排序,就好像是字典的索引目錄。非簇索引不對數據排序,它只保存了數據的指針地址。向一個帶簇索引的表中插入數據,當數據頁達到100%時,由于頁面沒有空間插入新的的紀錄,這時就會發生分頁,SQL?Server?將大約一半的數據從滿頁中移到空頁中,從而生成兩個半的滿頁。這樣就有大量的數據空間。簇索引是雙向鏈表,在每一頁的頭部保存了前一頁、后一頁地址以及分頁后數據移動的地址,由于新頁可能在數據庫文件中的任何地方,因此頁面的鏈接不一定指向磁盤的下一個物理頁,鏈接可能指向了另一個區域,這就形成了分塊,從而減慢了系統的速度。對于帶簇索引和非簇索引的表來說,非簇索引的關鍵字是指向簇索引的,而不是指向數據頁的本身。
為了克服數據分塊帶來的負面影響,需要重構表的索引,這是非常費時的,因此只能在需要時進行。可以通過DBCC?SHOWCONTIG來確定是否需要重構表的索引。
2.?DBCC?SHOWCONTIG用法
下面舉例來說明DBCC?SHOWCONTIG和DBCC?REDBINDEX的使用方法。以應用程序中的Employee數據表作為例子,在?SQL?Server的Query?analyzer輸入命令:
use?database_name
declare?@table_id?int
set?@table_id=object_id('Employee')
dbcc?showcontig(@table_id)
輸出結果:
DBCC?SHOWCONTIG?scanning?'Employee'?table...
Table:?'Employee'?(1195151303);?index?ID:?1,?database?ID:?53
TABLE?level?scan?performed.
-?Pages?Scanned................................:?179
-?Extents?Scanned..............................:?24
-?Extent?Switches..............................:?24
-?Avg.?Pages?per?Extent........................:?7.5
-?Scan?Density?[Best?Count:Actual?Count].......:?92.00%?[23:25]
-?Logical?Scan?Fragmentation?..................:?0.56%
-?Extent?Scan?Fragmentation?...................:?12.50%
-?Avg.?Bytes?Free?per?Page.....................:?552.3
-?Avg.?Page?Density?(full).....................:?93.18%
DBCC?execution?completed.?If?DBCC?printed?error?messages,?contact?your?system?administrator.
通過分析這些結果可以知道該表的索引是否需要重構。如下描述了每一行的意義:
信息???????????????????????????????????????????描述
Pages?Scanned????????????????????表或索引中的長頁數
Extents?Scanned?????????????????表或索引中的長區頁數
Extent?Switches??????????????????DBCC遍歷頁時從一個區域到另一個區域的次數
Avg.?Pages?per?Extent?????????相關區域中的頁數
Scan?Density[Best?Count:Actual?Count]
Best?Count是連續鏈接時的理想區域改變數,Actual?Count是實際區域改變數,Scan?Density為100%表示沒有分塊。
Logical?Scan?Fragmentation???掃描索引頁中失序頁的百分比
Extent?Scan?Fragmentation????不實際相鄰和包含鏈路中所有鏈接頁的區域數
Avg.?Bytes?Free?per?Page???????掃描頁面中平均自由字節數
Avg.?Page?Density?(full)?????????平均頁密度,表示頁有多滿
從上面命令的執行結果可以看的出來,Best?count為23?而Actual?Count為25這表明orders表有分塊需要重構表索引。下面通過DBCC?DBREINDEX來重構表的簇索引。
3.?DBCC?DBREINDEX?用法
重建指定數據庫中表的一個或多個索引。
語法
DBCC?DBREINDEX
(????[?'database.owner.table_name'
[?,?index_name
[?,?fillfactor?]
]
]
)
參數
'database.owner.table_name'
是要重建其指定的索引的表名。數據庫、所有者和表名必須符合標識符的規則。有關更多信息,請參見使用標識符。如果提供?database?或?owner?部分,則必須使用單引號?(')?將整個?database.owner.table_name?括起來。如果只指定?table_name,則不需要單引號。
index_name
是要重建的索引名。索引名必須符合標識符的規則。如果未指定?index_name?或指定為?'?',就要對表的所有索引進行重建。
fillfactor
是創建索引時每個索引頁上要用于存儲數據的空間百分比。fillfactor?替換起始填充因子以作為索引或任何其它重建的非聚集索引(因為已重建聚集索引)的新默認值。如果?fillfactor?為?0,DBCC?DBREINDEX?在創建索引時將使用指定的起始?fillfactor。
同樣在Query?Analyzer中輸入命令:
dbcc?dbreindex('database_name.dbo.Employee','',90)
然后再用DBCC?SHOWCONTIG查看重構索引后的結果:
DBCC?SHOWCONTIG?scanning?'Employee'?table...
Table:?'Employee'?(1195151303);?index?ID:?1,?database?ID:?53
TABLE?level?scan?performed.
-?Pages?Scanned................................:?178
-?Extents?Scanned..............................:?23
-?Extent?Switches..............................:?22
-?Avg.?Pages?per?Extent........................:?7.7
-?Scan?Density?[Best?Count:Actual?Count].......:?100.00%?[23:23]
-?Logical?Scan?Fragmentation?..................:?0.00%
-?Extent?Scan?Fragmentation?...................:?0.00%
-?Avg.?Bytes?Free?per?Page.....................:?509.5
-?Avg.?Page?Density?(full).....................:?93.70%
DBCC?execution?completed.?If?DBCC?printed?error?messages,?contact?your?system?administrator.
通過結果我們可以看到Scan?Denity為100%
1.合理使用索引
索引是數據庫中重要的數據結構,它的根本目的就是為了提高查詢效率。現在大多數的數據庫產品都采用IBM最先提出的ISAM索引結構。索引的使用要恰到好處,其使用原則如下:
●在經常進行連接,但是沒有指定為外鍵的列上建立索引,而不經常連接的字段則由優化器自動生成索引。
●在頻繁進行排序或分組(即進行group?by或order?by操作)的列上建立索引。
●在條件表達式中經常用到的不同值較多的列上建立檢索,在不同值少的列上不要建立索引。比如在雇員表的“性別”列上只有“男”與“女”兩個不同值,因此就無必要建立索引。如果建立索引不但不會提高查詢效率,反而會嚴重降低更新速度。
●如果待排序的列有多個,可以在這些列上建立復合索引(compound?index)。
●使用系統工具。如Informix數據庫有一個tbcheck工具,可以在可疑的索引上進行檢查。在一些數據庫服務器上,索引可能失效或者因為頻繁操作而使得讀取效率降低,如果一個使用索引的查詢不明不白地慢下來,可以試著用tbcheck工具檢查索引的完整性,必要時進行修復。另外,當數據庫表更新大量數據后,刪除并重建索引可以提高查詢速度。
(1)在下面兩條select語句中:
select?*?from?table1??where??field1<=10000?and?field1>=0;
select?*?from?table1??where??field1>=0?and?field1<=10000;
如果數據表中的數據field1都>=0,則第一條select語句要比第二條select語句效率高的多,因為第二條select語句的第一個條件耗費了大量的系統資源。
第一個原則:在where子句中應把最具限制性的條件放在最前面。
(2)在下面的select語句中:
select?*?from?tab??where??a=…?and?b=…?and?c=…;
若有索引index(a,b,c),則where子句中字段的順序應和索引中字段順序一致。
第二個原則:where子句中字段的順序應和索引中字段順序一致。
以下假設在field1上有唯一索引I1,在field2上有非唯一索引I2。
(3)?select?field3,field4?from?tb?where?field1='sdf'????????快
select?*?from?tb?where?field1='sdf'??????慢,
因為后者在索引掃描后要多一步ROWID表訪問。
(4)?select?field3,field4?from?tb?where?field1>='sdf'????????快
select?field3,field4?from?tb?where?field1>'sdf'????????慢
因為前者可以迅速定位索引。
(5)?select?field3,field4?from?tb?where?field2?like?'R%'????快
select?field3,field4?from?tb?where?field2?like?'%R'????慢,
因為后者不使用索引。
(6)?使用函數如:
select?field3,field4?from?tb?where?upper(field2)='RMN'不使用索引。
如果一個表有兩萬條記錄,建議不使用函數;如果一個表有五萬條以上記錄,嚴格禁止使用函數!兩萬條記錄以下沒有限制。
(7)?空值不在索引中存儲,所以
select?field3,field4?from?tb?where?field2?is[not]?null不使用索引。
(8)?不等式如
select?field3,field4?from?tb?where?field2!='TOM'不使用索引。
相似地,
select?field3,field4?from?tb?where?field2?not?in('M','P')不使用索引。
(9)?多列索引,只有當查詢中索引首列被用于條件時,索引才能被使用。
(10)??MAX,MIN等函數,如
Select?max(field2)?from?tb使用索引。所以,如果需要對字段取max,min,sum等,應該加索引。
一次只使用一個聚集函數,如:
select?“min”=min(field1),?“max”=max(field1)??from?tb
不如:select?“min”=(select?min(field1)?from?tb)?,?“max”=(select?max(field1)?from?tb)
(11)?重復值過多的索引不會被查詢優化器使用。而且因為建了索引,修改該字段值時還要修改索引,所以更新該字段的操作比沒有索引更慢。
(12)?索引值過大(如在一個char(40)的字段上建索引),會造成大量的I/O開銷(甚至會超過表掃描的I/O開銷)。因此,盡量使用整數索引。?Sp_estspace可以計算表和索引的開銷。
(13)?對于多列索引,order?by的順序必須和索引的字段順序一致。
(14)?在sybase中,如果order?by的字段組成一個簇索引,那么無須做order?by。記錄的排列順序是與簇索引一致的。
---使用索引優化數據庫查詢效率
1.不宜創建索引的情形
(1)經常插入,修改和刪除的表
(2)數據量比較小的表,因為查詢優化器在搜索索引時所花費的時間可能會大于遍歷全表的數據所需要的時間
2.適合創建索引的情形
(1)為where子句中出現的列創建索引
(2)創建組合索引
(3)為group?by?子句中出現的列創建索引
3.聚集索引的設計原則
(1)該列的數值是唯一的……