在瞭解了索引的基礎知識及B+樹索引的原理後(如需複習請點這裡),這一節我們瞭解一下有哪些建索引的原則,來指導我們去建索引。
建索引的原則
1. 聯合索引
我們可能聽一些資料庫方面的專業人士說過:“把 Where 條件裡面的列都建上索引”,從而給每個列給每個列建獨立的索引,這個理解是非常錯誤的。
如果 Where 條件裡有多種組合的查詢條件,可以嘗試建聯合索引來減少索引數量,同時提升查詢效能。
2. 覆蓋索引
普通索引查到主鍵後,回到主鍵索引搜尋的過程,稱為回表。
當使用普通索引查詢時,普通索引有我們所需結果的所有資訊(欄位),就可以直接提供查詢結果,而不需要回表。也就是說,在這個查詢裡,索引已經被“覆蓋了”查詢需求,稱為覆蓋索引。
mysql> create table T (
ID int primary key,
k int NOT NULL DEFAULT 0,
s varchar(16) NOT NULL DEFAULT '',
index k(k))
engine=InnoDB;
select ID from T where k between 3 and 5
上面的例子中,k索引樹上已經包括了ID的值,就不需要回表了。
由於覆蓋索引可以減少樹的搜尋次數,顯著提升查詢效能,所以使用覆蓋索引是一個常用的效能優化手段。
在使用覆蓋索引時,要注意如果需要返回的欄位較多,就要權衡空間和時間。因為覆蓋索引要覆蓋較多欄位就需要更多的空間。
3. 最左字首原則
假設我們有2個欄位,例如身份證號和地址,現在有三種查詢需求:按身份證號查詢、按地址查詢,以及按身份證號和地址查詢。
如果每種需要都要建立一個索引,就顯得有利浪費。
從上一篇文章裡,我們瞭解到B+樹的特點是有序的,因此我們可以只建立兩個索引即滿足上面的三種需求,分別是(身份證號,地址)和(地址)。這就是B+樹索引的“最左字首”原則。
在建立聯合索引的時候,如何安排索引內的欄位順序:
- 第一原則是,如果通過調整順序,可以少維護一個索引,那麼這個順序往往就是需要優先考慮採用的。
- 第二原則是,空間。當兩個欄位存在一個明顯比另一個大時,例如name和age,明顯name要比age要大,此時應該建一個(name,age)的聯合索引和一個(age)的單欄位索引。
4. 字首索引和索引選擇性
有時候需要索引很長的字元列,這會讓索引變得大且慢。一種策略是模擬雜湊索引。
通常可以索引開始的部分字元,這樣可以大大節約索引空間,從而提高索引效率。但這樣也會降低索引的選擇性。
索引的選擇性指,不重複的索引值(也稱為基數,cardinality)和資料表的記錄總數(T)的比值,範圍從1/T到1之間。
如何確定字首索引的長度及建立字首索引
假設有個訂單表,其中有包括城市名稱的欄位(city,且城市名稱為英文),下面來介紹一下如何確定字首索引的長度。
mysql> create table orders (
ID int primary key,
city varchar(16) NOT NULL
)engine=InnoDB;
一種方法是計算完整列的選擇性,並使字首的選擇性接近於完整列的選擇性。
計算完整列的選擇性:
mysql> SELECT COUNT(DISTINCT city)/COUNT(*) FROM orders;
在同一個查詢中計算不同字首長度的選擇性:
mysql> SELECT COUNT(DISTINCT LEFT(city, 3))/COUNT(*) AS sel3,
COUNT(DISTINCT LEFT(city, 4))/COUNT(*) AS sel4,
COUNT(DISTINCT LEFT(city, 5))/COUNT(*) AS sel5,
COUNT(DISTINCT LEFT(city, 6))/COUNT(*) AS sel6,
COUNT(DISTINCT LEFT(city, 7))/COUNT(*) AS sel7
FROM orders;
建立字首索引的方法:
mysql> ALTER TABLE orders ADD KEY (city(7));
5. 選擇合適的索引順序
在聯合索引中,索引列的順序是按照從左到右逐列進行排序的。因此索引可以按照升序或降序進行掃描,以滿足精確符合列順序的 ORDERY BY、GROUP BY 和 DISTINCT 等子句的查詢需求。
因此聯合索引的列順序很重要。
如何選擇索引的列順序有一個經驗法則:將選擇性最高的列放到索引最前列。
這個經驗法則在某些場景下有用,但可能在另外場景下,可能就沒效了,這要根據具體情況進行分析。