為什麼資料庫索引查詢會快
一、使用索引的好處
建立索引可以大大提高系統的效能。第一,通過建立唯一性索引,可以保證資料庫表中每一行資料的唯一性。第二,可以大大加快資料的檢索速度,這也是建立索引的最主要的原因。第三,可以加速表和表之間的連線,特別是在實現資料的參考完整性方面特別有意義。第四,在使用分組和排序子句進行資料檢索時,同樣可以顯著減少查詢中分組和排序的時間。第五,通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的效能。
二、索引的原理
資料在磁碟上是以塊的形式儲存的。為確保對磁碟操作的原子性,訪問資料的時候會一併訪問所有資料塊。磁碟上的這些資料塊與連結串列類似,即它們都包含一個數據段和一個指標,指標指向下一個節點(資料塊)的記憶體地址,而且它們都不需要連續儲存(即邏輯上相鄰的資料塊在物理上可以相隔很遠)。
鑑於很多記錄只能做到按一個欄位排序,所以要查詢某個未經排序的欄位,就需要使用線性查詢,即要訪問N/2個數據塊,其中N指的是一個表所涵蓋的所有資料塊。如果該欄位是非鍵欄位(也就是說,不包含唯一值),那麼就要搜尋整個表空間,即要訪問全部N個數據塊。
然而,對於經過排序的欄位,可以使用二分查詢,因此只要訪問log2 N個數據塊。同樣,對於已經排過序的非鍵欄位,只要找到更大的值,也就不用再搜尋表中的其他資料塊了。這樣一來,效能就會有實質性的提升。
首先,來看一個示例資料庫表的模式:
欄位名資料型別在磁碟上的大小
id (Primary key)Unsigned INT 4位元組
firstName Char(50)50位元組
lastName Char(50)50位元組
emailAddress Char(100)100位元組
注意:這裡用char
而不用varchar
是為了精確地描述資料佔用磁碟的大小。這個示例資料庫中包含500萬行記錄,而且沒有建立索引。接下來我們就分析針對這個表的兩個查詢:一個查詢使用id
(經過排序的鍵欄位),另一個查詢使用firstName
(未經排序的非鍵欄位)。
示例分析一
對於這個擁有r = 5 000 000條記錄的示例資料庫,在磁碟上要為每條記錄分配 R = 204位元組的固定儲存空間。這個表儲存在MyISAM資料庫中,而這個資料庫預設的資料庫塊大小為 B = 1024位元組。於是,我們可計算出這個表的分塊因數為 bfr = (B/R) = 1024/204 = 5,即磁碟上每個資料塊儲存5條記錄。那麼,儲存整個表所需的資料塊數就是 N = (r/bfr) = 5000000/5 = 1 000 000。
使用線性查詢搜尋id欄位——這個欄位是鍵欄位(每個欄位的值唯一),需要訪問 N/2 = 500 000個數據塊才能找到目標值。不過,因為這個欄位是經過排序的,所以可以使用二分查詢法,而這樣平均只需要訪問log2 1000000 = 19.93 = 20 個塊。顯然,這會給效能帶來極大的提升。
再來看看firstName欄位,這個欄位是未經排序的,因此不可能使用二分查詢,況且這個欄位的值也不是唯一的,所以要從表的開頭查詢末尾,即要訪問 N = 1 000 000個數據塊。這種情況通過建立索引就能得到改善。
如果一條索引記錄只包含索引欄位和一個指向原始記錄的指標,那麼這條記錄肯定要比它所指向的包含更多欄位的記錄更小。也就是說,索引本身佔用的磁碟空間比原來的表更少,因此需要遍歷的資料塊數也比搜尋原來的表更少。以下是firstName欄位索引的模式:
欄位名資料型別在磁碟上的大小
firstName Char(50)50位元組(記錄指標)Special4位元組
注意:在MySQL中,根據表的大小,指標的大小可能是2、3、4或5位元組。
示例分析二
對於這個擁有r = 5 000 000條記錄的示例資料庫,每條索引記錄要佔用 R = 54位元組磁碟空間,而且同樣使用預設的資料塊大小 B = 1024位元組。那麼索引的分塊因數就是 bfr = (B/R) = 1024/54 = 18。最終這個表的索引需要佔用 N = (r/bfr) = 5000000/18 = 277 778個數據塊。
現在,再搜尋firstName欄位就可以使用索引來提高效能了。對索引使用二分查詢,需要訪問 log2 277778 = 18.09 = 19個數據塊。再加上為找到實際記錄的地址還要訪問一個數據塊,總共要訪問 19 + 1 = 20個數據塊,這與搜尋未索引的表需要訪問277 778個數據塊相比,不啻於天壤之別。
三、什麼時候建索引
索引是建立在資料庫表中的某些列的上面。因此,在建立索引的時候,應該仔細考慮在哪些列上可以建立索引,在哪些列上不能建立索引。一般來說,應該在這些列上建立索引,例如:在經常需要搜尋的列上,可以加快搜索的速度;在作為主鍵的列上,強制該列的唯一性和組織表中資料的排列結構;在經常用在連線的列上,這些列主要是一些外來鍵,可以加快連線的速度;在經常需要根據範圍進行搜尋的列上建立索引,因為索引已經排序,其指定的範圍是連續的;在經常需要排序的列上建立索引,因為索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間;在經常使用在WHERE子句中的列上面建立索引,加快條件的判斷速度。
同樣,對於有些列不應該建立索引。一般來說,不應該建立索引的的這些列具有下列特點:第一,對於那些在查詢中很少使用或者參考的列不應該建立索引。這是因為,既然這些列很少使用到,因此有索引或者無索引,並不能提高查詢速度。相反,由於增加了索引,反而降低了系統的維護速度和增大了空間需求。第二,對於那些只有很少資料值的列也不應該增加索引。這是因為,由於這些列的取值很少,例如人事表的性別列,在查詢的結果中,結果集的資料行佔了表中資料行的很大比例,即需要在表中搜索的資料行的比例很大。增加索引,並不能明顯加快檢索速度。第三,對於那些定義為text,
image和bit資料型別的列不應該增加索引。這是因為,這些列的資料量要麼相當大,要麼取值很少。第四,當修改效能遠遠大於檢索效能時,不應該建立索引。這是因為,修改效能和檢索效能是互相矛盾的。當增加索引時,會提高檢索效能,但是會降低修改效能。當減少索引時,會提高修改效能,降低檢索效能。因此,當修改效能遠遠大於檢索效能時,不應該建立索引。
四、索引的常用種類及建立方法
這是最基本的索引,它沒有任何限制。它有以下幾種建立方式:
◆建立索引
CREATE INDEX indexName ON mytable(username(length)); 如果是CHAR,VARCHAR型別,length可以小於欄位實際長度;如果是BLOB和TEXT型別,必須指定 length,下同。
◆修改表結構
ALTER mytable ADD INDEX [indexName] ON (username(length)) ◆建立表的時候直接指定
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, INDEX [indexName] (username(length)) ); 刪除索引的語法:
DROP INDEX [indexName] ON mytable;
(2)唯一索引
它與前面的普通索引類似,不同的就是:索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。它有以下幾種建立方式:
◆建立索引
CREATE UNIQUE INDEX indexName ON mytable(username(length)) ◆修改表結構
ALTER mytable ADD UNIQUE [indexName] ON (username(length)) ◆建立表的時候直接指定
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, UNIQUE [indexName] (username(length)) );
(3)主鍵索引
它是一種特殊的唯一索引,不允許有空值。一般是在建表的時候同時建立主鍵索引:
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, PRIMARY KEY(ID) ); 當然也可以用 ALTER 命令。記住:一個表只能有一個主鍵。
(4)組合索引
為了形象地對比單列索引和組合索引,為表新增多個欄位:
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, city VARCHAR(50) NOT NULL, age INT NOT NULL ); 為了進一步榨取MySQL的效率,就要考慮建立組合索引。就是將 name, city, age建到一個索引裡:
ALTER TABLE mytable ADD INDEX name_city_age (name(10),city,age); 建表時,usernname長度為 16,這裡用 10。這是因為一般情況下名字的長度不會超過10,這樣會加速索引查詢速度,還會減少索引檔案的大小,提高INSERT的更新速度。
如果分別在 usernname,city,age上建立單列索引,讓該表有3個單列索引,查詢時和上述的組合索引效率也會大不一樣,遠遠低於我們的組合索引。雖然此時有了三個索引,但MySQL只能用到其中的那個它認為似乎是最有效率的單列索引。
建立這樣的組合索引,其實是相當於分別建立了下面三組組合索引:
usernname,city,age usernname,city usernname 為什麼沒有 city,age這樣的組合索引呢?這是因為MySQL組合索引“最左字首”的結果。簡單的理解就是隻從最左面的開始組合。並不是只要包含這三列的查詢都會用到該組合索引,下面的幾個SQL就會用到這個組合索引:
SELECT * FROM mytable WHREE username="admin" AND city="鄭州" SELECT * FROM mytable WHREE username="admin" 而下面幾個則不會用到:
SELECT * FROM mytable WHREE age=20 AND city="鄭州" SELECT * FROM mytable WHREE city="鄭州"