一句話理解數據庫索引

摘要: 通過一個非常簡單的例子解釋我們爲什麼需要索引?沒有索引會發生什麼?

首先通過一個非常簡單的例子來解釋爲什麼你需要數據庫索引。

假設我們有一張數據表Emplyee,該表有三列:

Employee_Name,Employee_Age,Employee_Address

表中有幾萬條記錄。現在,我們要執行下面這條查詢語句,查找出所有名字叫“Jesus”的員工的詳細信息:


SELECT * FROM Employee

WHERE Employee_Name = 'Jesus'

沒有索引會發生什麼?

當我們執行上面這條查詢後,數據庫中究竟發生了什麼?數據庫系統會逐行的遍歷整張表,對於每一行都要檢查其Employee_Name字段是否等於“Jesus”。因爲我們要查找所有名字爲“Jesus”的員工,所以當我們發現了一條名字是“Jesus”的記錄後,並不能停止繼續查找,因爲可能有其他員工也叫“Jesus”。這就意味着,對於表中的幾萬條記錄,數據庫每一條都要檢查。這就是所謂的“全表掃描”( full table scan)。

數據庫索引是怎麼提高查詢效率的?

也許你在抱怨,這麼簡單的事情還要做全表掃描,這簡直就是用人眼檢查整個表,效率太低了!數據庫就不能更聰明點嗎?也許你已經根據文章標題猜到了,在這裏使用索引會大有幫助。索引的最大作用就是加快查詢速度,它能從根本上減少需要掃表的記錄/行的數量。

一句話理解什麼是索引!

索引就是數據結構!

進一步說該數據結構中存儲了這張表中某一列的所有值,就是說索引是基於數據表中的某一列創建的。再囉嗦一遍:一個索引是由表中某一列上的數據組成,並且這些數據存儲在某個數據結構中。最後一遍:索引就是數據結構。嗯,記住這句話吧!

索引使用哪種數據結構?

B- 樹是用於索引最常見的數據結構。這是因爲B-樹有較好的時間效率,在查找、刪除、插入操作上其時間複雜度都是對數階,即O(logn)。另外一個重要原因是,存儲在B-樹上的數據是有序的。通常,創建索引時使用哪種數據結構是由數據庫決定的。但是,有些數據庫,你可以指定使用哪種數據結構創建索引。

Hash索引怎麼工作的?

Hash表是另外一種可以被用作索引的數據結構,這種索引稱作hash索引。Hash表的查詢效率是非常高的,尤其對比較字符串相等的查詢,如果使用了hash索引,那麼查詢速度是極快的。例如,前面討論過的查詢語句(SELECT * FROM Employee WHERE Employee_Name = ‘Jesus’),如果在Employee_Name列上創建hash索引,會顯著提高該查詢的效率。Hash索引的原理是,將列值作爲hash表的key,而value中保存行的指針。Hash表就是一個關聯數組(associative array,關聯數組又稱映射Map或字典Dictionary),典型的結構看起來是這個樣子:

Jesus -> 0x28939

0x28939指向“Jesus”這一行在內存中的地址(Mysql 只有memory引擎支持Hash索引)。使用hash索引直接通過"Jesus"獲取這一行在內存中的地址指針,明顯比通過全表掃描查找出Employee_Name=’Jesus‘的行要快的多。

Hash索引的缺點!

Hash表是無序的數據結構,在很多情況下,使用hash索引並不能提高效率。例如,你想查找出所有年齡小於40的員工,就不能使用hash索引。因爲Hash索引只適用於鍵值對查詢,即等值查詢(例如"WHERE name=`Jesus`),不能用於範圍查詢。Hash表中的鍵值映射隱含的告訴了你,hash表中的鍵並沒有按照特定的順序排序。而且hash表不如B- 樹靈活,所以數據庫系統通常會選擇B-樹作爲索引的默認數據結構而不是hash表。

還有其他類型的索引嗎?

R- 樹索引通常用來解決空間問題。例如,你要查找“距離我2km以內的星巴克”,類似這種查詢使用R-樹索引會有很好的性能提升。空間數據庫代表有PostGIS,MySQL Spatial。

還有一種位圖(bitmap)索引,位圖索引一般用在布爾類型的列上。這些列都具有低選擇性(low selectivity)的特點。因爲布爾類型的列只有1和0(True和False)兩種值,假設這張表有10000條記錄,該列的選擇性爲2/10000 ×100%=0.02%,這麼低的選擇性很適合很使用位圖索引。更多關於索引的選擇性問題,大力戳《不知道"選擇性"怎麼能說懂索引呢》

索引是怎麼提高查詢效率的?

索引的本質上是一個存儲列值的數據結構。如果在某列上使用了B-樹索引,那麼這些列值在索引中是被排過序的,有序的值是索引能提高查詢性能的主要原因。

當我們在Employee_Name列上創建了B-樹索引後,再去執行前面提到的SQL語句時,數據庫就不用再對Emplyee表做全表掃描了,而是直接從索引中查找名字叫“Jesus”的員工。由於B-樹索引會把所有員工的名字按字母表順序進行排序,這樣以'J'開始的名字都會相鄰,查找起來就會很快。值得注意的是,索引除了保存員工姓名還會保存該員工在數據表中所在行的指針,這樣就可以檢索到行中其他列的數據,以獲取員工更多的信息。

索引中保存了什麼?

現在你知道了索引是創建在表中的某列上,索引中保存了該列的所有數據。這裏要理解的是,索引並不保存其他列的數據。例如我們在Employee_Name列上創建了索引,同一張表的Employee_Age 、Employee_Address列的數據不會被保存到索引中。如果我們在索引中保存了其他列的數據,那無異於對整張表做copy,浪費空間不說效率還低,這是非常愚蠢的做法。

索引中保存了行的指針

那麼,問題來了?當我們在索引中查找到“Jesus”後,怎麼獲取這一行中的其他列值(比如Jesus的年齡和地址)。其實很簡單,因爲索引中保存了關聯行在表中的位置的指針。也就是說,索引除了保存列值之外,還保存了與該列值關聯的行在表中的位置信息。因此,Employee_Name索引中的值(或節點)看起來大概是這個樣子 (“Jesus”, 0x82829),0x82829就是“Jesus”這一行在磁盤上的存儲地址(指針)。如果沒有這個指針,只有一個單獨的列值,對我們是沒意義的,因爲我們無法獲取這一行的其他信息。

數據庫怎麼知道什麼時候使用索引?

當我們執行這樣一條查詢時

SELECT * FROM Employee
WHERE Employee_Name = ‘Jesus’

數據庫首先會檢查Employ_Name列上有沒有創建索引。如果Employee_Name列上有索引,數據庫還要判斷是否應該使用索引檢索要查找的值,因爲有些情況,做全表掃描要比使用數據庫索引高效。有些情況是什麼情況呢?大力戳《全表掃描!你的數據庫有點弱智》

可以強制讓數據庫使用索引查詢嗎?

通常,你不需要告訴數據庫什麼時候使用索引,這由數據庫自己決定。但是,對於大多數數據庫(例如Oracle和Mysql),你可以強制讓數據庫使用索引,例如Mysql可以通過“force index”來強制使用索引。

怎麼創建索引?

這裏我們爲本文中一直提到的Emloyee_Name列創建索引:

CREATE INDEX name_index

ON Employee (Employee_Name)

怎麼創建聯合索引?

我們也可以基於Employee表的兩列創建索引:

CREATE INDEX name_index

ON Employee (Employee_Name, Employee_Age)

對於數據庫索引有沒有好的類比?

圖片:書籍《更喜歡攝影》的目錄頁

這裏有個很經典的類比,把數據庫索引想象成一本書的目錄。假設你有一本介紹狗的書,現在你要看關於哈士奇的章節。你會去瀏覽整本書嗎?這相當於數據庫的全表掃描,浪費時間啊。然而你可以直接查看這本書的目錄,它會告訴你關於哈士奇的章節在第幾頁。就像書的目錄中包含頁碼,類似的,數據庫的索引包含指針,指向你用SQL語句正在查找的那一行。

使用索引有什麼代價?

那麼,創建數據庫索引有什麼缺點?首先,佔用空間。你的表越大,索引需要的空間就越大。另外會影響數據庫性能,你對數據表進行增刪改操作,同樣的操作還要在索引上執行一次。

請記住,索引中始終維護着和被索引列相同的數據

基本原則

只在頻繁查詢的列上創建索引!

英文原文:http://www.programmerinterview.com/index.php/database-sql/what-is-an-index/

發佈了6 篇原創文章 · 獲贊 8 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章