首先明白爲何索引會增長速度,DB在執行一條Sql語句的時候,默認的方式是根據搜索條件進行全表掃描,遇到匹配條件的就加入搜索結果集合。若是咱們對某一字段增長索引,查詢時就會先去索引列表中一次定位到特定值的行數,大大減小遍歷匹配的行數,因此能明顯增長查詢的速度。html
添加索引的話,首先去索引列表中查詢,而咱們的索引列表是B類樹的數據結構,查詢的時間複雜度爲O(log2N),定位到特定值得行就會很是快,因此其查詢速度就會很是快。sql
爲何說B+-tree比B 樹更適合實際應用中操做系統的文件索引和數據庫索引?數據庫
1) B+-tree的磁盤讀寫代價更低安全
B+-tree的內部結點並無指向關鍵字具體信息的指針。所以其內部結點相對B 樹更小。若是把全部同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的須要查找的關鍵字也就越多。相對來講IO讀寫次數也就下降了。服務器
舉個例子,假設磁盤中的一個盤塊容納16bytes,而一個關鍵字2bytes,一個關鍵字具體信息指針2bytes。一棵9階B-tree(一個結點最多8個關鍵字)的內部結點須要2個盤快。而B+ 樹內部結點只須要1個盤快。當須要把內部結點讀入內存中的時候,B 樹就比B+ 樹多一次盤塊查找時間(在磁盤中就是盤片旋轉的時間)。數據結構
2) B+-tree的查詢效率更加穩定oracle
因爲非終結點並非最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。因此任何關鍵字的查找必須走一條從根結點到葉子結點的路。全部關鍵字查詢的路徑長度相同,致使每個數據的查詢效率至關。數據庫設計
讀者點評
fanyy1991(csdn用戶名)道:我的以爲這兩個緣由都不是主要緣由。數據庫索引採用B+樹的主要緣由是 B樹在提升了磁盤IO性能的同時並無解決元素遍歷的效率低下的問題。正是爲了解決這個問題,B+樹應運而生。B+樹只要遍歷葉子節點就能夠實現整棵樹的遍歷。並且在數據庫中基於範圍的查詢是很是頻繁的,而B樹不支持這樣的操做(或者說效率過低)。函數
上述那個問題轉載自:從B樹、B+樹、B*樹談到R 樹post
那麼在任什麼時候候都應該加索引麼?這裏有幾個反例:一、若是每次都須要取到全部表記錄,不管如何都必須進行全表掃描了,那麼是否加索引也沒有意義了。二、對非惟一的字段,例如「性別」這種大量重複值的字段,增長索引也沒有什麼意義。三、對於記錄比較少的表,增長索引不會帶來速度的優化反而浪費了存儲空間,由於索引是須要存儲空間的,並且有個致命缺點是對於update/insert/delete的每次執行,字段的索引都必須從新計算更新。
那麼在何時適合加上索引呢?咱們看一個Mysql手冊中舉的例子,這裏有一條sql語句:
SELECT c.companyID, c.companyName FROM Companies c, User u WHERE c.companyID = u.fk_companyID AND c.numEmployees >= 0 AND c.companyName LIKE '%i%' AND u.groupID IN (SELECT g.groupID FROM Groups g WHERE g.groupLabel = 'Executive')
這條語句涉及3個表的聯接,而且包括了許多搜索條件好比大小比較,Like匹配等。在沒有索引的狀況下Mysql須要執行的掃描行數是 77721876行。而咱們經過在companyID和groupLabel兩個字段上加上索引以後,掃描的行數只須要134行。在Mysql中能夠經過 Explain Select來查看掃描次數。能夠看出來在這種聯表和複雜搜索條件的狀況下,索引帶來的性能提高遠比它所佔據的磁盤空間要重要得多。
那麼索引是如何實現的呢?大多數DB廠商實現索引都是基於一種數據結構——B樹。oracle實現索引的數據結構是B*樹。具體關於B樹、B+樹、B*樹的講解能夠查看另外一篇博文:樹
能夠看到在這棵B樹搜索英文字母複雜度只爲o(m),在數據量比較大的狀況下,這樣的結構能夠大大增長查詢速度。然而有另一種數據結構查詢的虛度比B樹更快——散列表。Hash表的定義是這樣的:設全部可能出現的關鍵字集合爲u,實際發生存儲的關鍵字記爲k,而|k|比|u|小不少。散列方法是經過散列函數h將u映射到表T[0,m-1]的下標上,這樣u中的關鍵字爲變量,以h爲函數運算結果即爲相應結點的存儲地址。從而達到能夠在o(1)的時間內完成查找。
然而散列表有一個缺陷,那就是散列衝突,即兩個關鍵字經過散列函數計算出了相同的結果。設m和n分別表示散列表的長度和填滿的結點數,n/m爲散列表的填裝因子,因子越大,表示散列衝突的機會越大。
由於有這樣的缺陷,因此數據庫不會使用散列表來作爲索引的默認實現,Mysql宣稱會根據執行查詢格式嘗試將基於磁盤的B樹索引轉變爲和合適的散列索引以追求進一步提升搜索速度。我想其它數據庫廠商也會有相似的策略,畢竟在數據庫戰場上,搜索速度和管理安全同樣是很是重要的競爭點。
基本概念介紹:
索引
使用索引可快速訪問數據庫表中的特定信息。索引是對數據庫表中一列或多列的值進行排序的一種結構,例如 employee 表的姓(lname)列。若是要按姓查找特定職員,與必須搜索表中的全部行相比,索引會幫助您更快地得到該信息。
索引提供指向存儲在表的指定列中的數據值的指針,而後根據您指定的排序順序對這些指針排序。數據庫使用索引的方式與您使用書籍中的索引的方式很類似:它搜索索引以找到特定值,而後順指針找到包含該值的行。
在數據庫關係圖中,您能夠在選定表的「索引/鍵」屬性頁中建立、編輯或刪除每一個索引類型。當保存索引所附加到的表,或保存該表所在的關係圖時,索引將保存在數據庫中。有關詳細信息,請參見建立索引。
注意;並不是全部的數據庫都以相同的方式使用索引。有關更多信息,請參見數據庫服務器注意事項,或者查閱數據庫文檔。
做爲通用規則,只有當常常查詢索引列中的數據時,才須要在表上建立索引。索引佔用磁盤空間,而且下降添加、刪除和更新行的速度。在多數狀況下,索引用於數據檢索的速度優點大大超過它的。
索引列
能夠基於數據庫表中的單列或多列建立索引。多列索引使您能夠區分其中一列可能有相同值的行。
若是常常同時搜索兩列或多列或按兩列或多列排序時,索引也頗有幫助。例如,若是常常在同一查詢中爲姓和名兩列設置判據,那麼在這兩列上建立多列索引將頗有意義。
肯定索引的有效性:
索引類型
根據數據庫的功能,能夠在數據庫設計器中建立三種索引:惟一索引、主鍵索引和彙集索引。有關數據庫所支持的索引功能的詳細信息,請參見數據庫文檔。
提示:儘管惟一索引有助於定位信息,但爲得到最佳性能結果,建議改用主鍵或惟一約束。
惟一索引是不容許其中任何兩行具備相同索引值的索引。
當現有數據中存在重複的鍵值時,大多數數據庫不容許將新建立的惟一索引與表一塊兒保存。數據庫還可能防止添加將在表中建立重複鍵值的新數據。例如,若是在 employee 表中職員的姓 (lname) 上建立了惟一索引,則任何兩個員工都不能同姓。
主鍵索引
數據庫表常常有一列或列組合,其值惟一標識表中的每一行。該列稱爲表的主鍵。
在數據庫關係圖中爲表定義主鍵將自動建立主鍵索引,主鍵索引是惟一索引的特定類型。該索引要求主鍵中的每一個值都惟一。當在查詢中使用主鍵索引時,它還容許對數據的快速訪問。
彙集索引
在彙集索引中,表中行的物理順序與鍵值的邏輯(索引)順序相同。一個表只能包含一個彙集索引。
若是某索引不是彙集索引,則表中行的物理順序與鍵值的邏輯順序不匹配。與非彙集索引相比,彙集索引一般提供更快的數據訪問速度。
創建方式和注意事項
最普通的狀況,是爲出如今where子句的字段建一個索引。爲方便講述,咱們先創建一個以下的表。
CREATE TABLE mytable (
id serial primary key,
category_id int not null default 0,
user_id int not null default 0,
adddate int not null default 0
);
若是你在查詢時經常使用相似如下的語句:
SELECT * FROM mytable WHERE category_id=1;
最直接的應對之道,是爲category_id創建一個簡單的索引:
CREATE INDEX mytable_categoryid
ON mytable (category_id);
OK.若是你有不止一個選擇條件呢?例如:
SELECT * FROM mytable WHERE category_id=1 AND user_id=2;
你的第一反應多是,再給user_id創建一個索引。很差,這不是一個最佳的方法。你能夠創建多重的索引。
CREATE INDEX mytable_categoryid_userid ON mytable (category_id,user_id);
注意到我在命名時的習慣了嗎?我使用"表名_字段1名_字段2名"的方式。你很快就會知道我爲何這樣作了。
如今你已經爲適當的字段創建了索引,不過,仍是有點不放心吧,你可能會問,數據庫會真正用到這些索引嗎?測試一下就OK,對於大多數的數據庫來講,這是很容易的,只要使用EXPLAIN命令:
EXPLAIN
SELECT * FROM mytable
WHERE category_id=1 AND user_id=2;
This is what Postgres 7.1 returns (exactly as I expected)
NOTICE: QUERY PLAN:
Index Scan using mytable_categoryid_userid on
mytable (cost=0.00..2.02 rows=1 width=16)
EXPLAIN
以上是postgres的數據,能夠看到該數據庫在查詢的時候使用了一個索引(一個好開始),並且它使用的是我建立的第二個索引。看到我上面命名的好處了吧,你立刻知道它使用適當的索引了。
接着,來個稍微複雜一點的,若是有個ORDER BY字句呢?無論你信不信,大多數的數據庫在使用order by的時候,都將會從索引中受益。
SELECT * FROM mytable
WHERE category_id=1 AND user_id=2
ORDER BY adddate DESC;
很簡單,就象爲where字句中的字段創建一個索引同樣,也爲ORDER BY的字句中的字段創建一個索引:
CREATE INDEX mytable_categoryid_userid_adddate
ON mytable (category_id,user_id,adddate);
注意: "mytable_categoryid_userid_adddate" 將會被截短爲
"mytable_categoryid_userid_addda"
CREATE
EXPLAIN SELECT * FROM mytable
WHERE category_id=1 AND user_id=2
ORDER BY adddate DESC;
NOTICE: QUERY PLAN:
Sort (cost=2.03..2.03 rows=1 width=16)
-> Index Scan using mytable_categoryid_userid_addda
on mytable (cost=0.00..2.02 rows=1 width=16)
EXPLAIN
看看EXPLAIN的輸出,數據庫多作了一個咱們沒有要求的排序,這下知道性能如何受損了吧,看來咱們對於數據庫的自身運做是有點過於樂觀了,那麼,給數據庫多一點提示吧。
爲了跳過排序這一步,咱們並不須要其它另外的索引,只要將查詢語句稍微改一下。這裏用的是postgres,咱們將給該數據庫一個額外的提示--在 ORDER BY語句中,加入where語句中的字段。這只是一個技術上的處理,並非必須的,由於實際上在另外兩個字段上,並不會有任何的排序操做,不過若是加入,postgres將會知道哪些是它應該作的。
EXPLAIN SELECT * FROM mytable
WHERE category_id=1 AND user_id=2
ORDER BY category_id DESC,user_id DESC,adddate DESC;
NOTICE: QUERY PLAN:
Index Scan Backward using
mytable_categoryid_userid_addda on mytable
(cost=0.00..2.02 rows=1 width=16)
EXPLAIN
如今使用咱們料想的索引了,並且它還挺聰明,知道能夠從索引後面開始讀,從而避免了任何的排序。
以上說得細了一點,不過若是你的數據庫很是巨大,而且每日的頁面請求達上百萬算,我想你會獲益良多的。不過,若是你要作更爲複雜的查詢呢,例如將多張表結合起來查詢,特別是where限制字句中的字段是來自不止一個表格時,應該怎樣處理呢?我一般都儘可能避免這種作法,由於這樣數據庫要將各個表中的東西都結合起來,而後再排除那些不合適的行,搞很差開銷會很大。
若是不能避免,你應該查看每張要結合起來的表,而且使用以上的策略來創建索引,而後再用EXPLAIN命令驗證一下是否使用了你料想中的索引。若是是的話,就OK。不是的話,你可能要創建臨時的表來將他們結合在一塊兒,而且使用適當的索引。
要注意的是,創建太多的索引將會影響更新和插入的速度,由於它須要一樣更新每一個索引文件。對於一個常常須要更新和插入的表格,就沒有必要爲一個不多使用的where字句單獨創建索引了,對於比較小的表,排序的開銷不會很大,也沒有必要創建另外的索引。
以上介紹的只是一些十分基本的東西,其實裏面的學問也很多,單憑EXPLAIN咱們是不能斷定該方法是否就是最優化的,每一個數據庫都有本身的一些優化器,雖然可能還不太完善,可是它們都會在查詢時對比過哪一種方式較快,在某些狀況下,創建索引的話也未必會快,例如索引放在一個不連續的存儲空間時,這會增長讀磁盤的負擔,所以,哪一個是最優,應該經過實際的使用環境來檢驗。
在剛開始的時候,若是表不大,沒有必要做索引,個人意見是在須要的時候才做索引,也可用一些命令來優化表,例如MySQL可用"OPTIMIZE TABLE"。
綜上所述,在如何爲數據庫創建恰當的索引方面,你應該有一些基本的概念了。
轉載自:http://www.jb51.net/article/27315.htm