mysql索引分析與優化

1. MySQL索引實現

在MySQL中,索引屬於存儲引擎級別的概念,不同存儲引擎對索引的實現方式是不同的,下面主要討論MyISAM和InnoDB兩個存儲引擎的索引實現方式。

MyISAM索引實現

MyISAM引擎使用B+Tree作爲索引結構,葉節點的data域存放的是數據記錄的地址。下圖是MyISAM索引的原理圖:

圖1

這裏設表一共有三列,假設我們以Col1爲主鍵,則圖1是一個MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重複。如果我們在Col2上建立一個輔助索引,則此索引的結構如下圖所示:

 

圖2 

同樣也是一顆B+Tree,data域保存數據記錄的地址。因此,MyISAM中索引檢索的算法爲首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其data域的值,然後以data域的值爲地址,讀取相應數據記錄。

MyISAM的索引方式也叫做“非聚集”的,之所以這麼稱呼是爲了與InnoDB的聚集索引區分。

InnoDB索引實現

雖然InnoDB也使用B+Tree作爲索引結構,但具體實現方式卻與MyISAM截然不同。

第一個重大區別是InnoDB的數據文件本身就是索引文件。從上文知道,MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在InnoDB中,表數據文件本身就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引。

圖3

圖3是InnoDB主索引(同時也是數據文件)的示意圖,可以看到葉節點包含了完整的數據記錄。這種索引叫做聚集索引。因爲InnoDB的數據文件本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),如果沒有顯式指定,則MySQL系統會自動選擇一個可以唯一標識數據記錄的列作爲主鍵,如果不存在這種列,則MySQL自動爲InnoDB表生成一個隱含字段作爲主鍵,這個字段長度爲6個字節,類型爲長整形。

第二個與MyISAM索引的不同是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB的所有輔助索引都引用主鍵作爲data域。例如,圖4爲定義在Col3上的一個輔助索引:

圖4

這裏以英文字符的ASCII碼作爲比較準則。聚集索引這種實現方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然後用主鍵到主索引中檢索獲得記錄。

瞭解不同存儲引擎的索引實現方式對於正確使用和優化索引都非常有幫助,例如知道了InnoDB的索引實現後,就很容易明白爲什麼不建議使用過長的字段作爲主鍵,因爲所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。再例如,用非單調的字段作爲主鍵在InnoDB中不是個好主意,因爲InnoDB數據文件本身是一顆B+Tree,非單調的主鍵會造成在插入新記錄時數據文件爲了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增字段作爲主鍵則是一個很好的選擇。

2. 高性能的索引策略

2.1 獨立的列

“獨立的列”是指索引列不能是表達式的一部分,也不能是函數的參數。

錯誤的寫法:


 

 

應該養成簡化where條件的習慣,始終將索引列單獨放在比較符號的一側。

正確的寫法:


 

 

2.2 索引選擇性

selectivity = distinct Values / total Rows

索引的選擇性是指,不重複的索引值和數據表的記錄總數的比值。索引的選擇性越高則查詢效率越高,因爲選擇性高的索引可以讓MySQL在查找時過濾掉更多的行。

唯一索引的選擇性是1,這是最好的索引選擇性,性能也是最好的。

 

2.3 前綴索引

對於BLOB、TEXT或者很長的VARCHAR類型的列,必須使用前綴索引,因爲MySQL不允許索引這些列的完整長度。

訣竅在於要選擇足夠長的前綴以保持較高的選擇性,同時又不能太長(以便節約空間)。前綴應該足夠長,以使得前綴索引的選擇性接近於索引整個列。

 

2.4 多列索引

一個常見的錯誤是,爲每個列創建獨立的索引,或者按照錯誤的順序創建多列索引。

錯誤的寫法:

複製代碼


 

複製代碼

在多個列上建立獨立的單列索引大部分情況下並不能提高MySQL的查詢性能。對於下面的查詢where條件,這兩個單列索引都是不好的選擇:


 

在老的MySQL版本中,MySQL會對這個查詢使用全表掃描。除非改寫成兩個查詢UNION的方式。


 

MySQL5.0和更新的版本引入了一種叫“索引合併”的策略,查詢能夠同時使用這兩個單列索引進行掃描,並將結果合併。這種算法有三個變種:OR條件的聯合(union),AND條件的相交(intersection),組合前兩種情況的聯合及相交。索引合併策略有時候是一種優化的結果,但實際上更多時候說明了表上的索引建得很糟糕:

(1)當出現服務器對多個索引做相交操作時(多個AND條件),通常意味着需要一個包含所有相關列的多列索引,而不是多個獨立的單列索引。

(2)當出現服務器對多個索引做聯合操作時(多個OR條件),通常需要耗費大量的CPU和內存資源在算法的緩存、排序和合並操作上。特別是當其中有些索引的選擇性不高,需要合併掃描返回的大量數據的時候。

(3)如果在explain中看到有索引合併,應該好好檢查一下查詢和表的結構,看是不是已經是最優的。

 

2.5 覆蓋索引

如果一個索引包含(或者說覆蓋)所有需要查詢的字段的值,就稱之爲“覆蓋索引”。MySQL利用索引返回select列表中的字段,而不必根據索引再次回表讀取數據頁。


 

不是所有類型的索引都可以成爲覆蓋索引。覆蓋索引必須要存儲索引列的值,而哈希索引、空間索引和全文索引等都不存儲索引列的值,所以MySQL只能用B-Tree索引做覆蓋索引。另外,不同的存儲引擎實現覆蓋索引的方式也不同,而且不是所有的引擎都支持覆蓋索引。

 

2.6 組合索引 

和覆蓋索引類似對查詢語句中多個常用字段建立索引,當然,創建組合索引並不是說就須要將查詢條件中的所有字段都放在一個索引中,還應該儘量讓一個索引被多個 Query 語句利用,儘量減少同一個表上的索引數量,減少因爲數據更新帶來的索引更新成本,同時還可以減少因爲索引所消耗的存儲空間。

 

2.7 儘量避免NULL

(1)儘可能把字段定義爲NOT NULL,可以放置一個默認值,如’’,0等。

(2)MySQL 難以優化NULL列。NULL列會使索引統計和值更加複雜。

(3)NULL列需要更多的存儲空間,還需要在MYSQL內部進行特殊處理。

(4)NULL列加索引,每條記錄都需要一個額外的字節,還導致MyISAM中固定大小的索引變成可變大小的索引。

 

3. 總結

在選擇索引和編寫利用這些索引的查詢時,有以下三個原則:

(1)單行訪問是很慢的。最好讀取的塊中能包含儘可能多所需要的行。使用索引可以創建位置引用以提升效率。

(2)按順序訪問範圍數據是很快的,這有兩個原因。第一,順序I/O不需要多次磁盤尋道,所以比隨機I/O快很多。第二,如果服務器能夠按需要順序讀取數據,那麼就不需要額外的排序操作,並且group by 查詢也無需在做排序和將行按組進行聚合計算了。

(3)索引覆蓋查詢是很快的。如果一個索引包含了查詢需要的列,那麼存儲引擎就不需要再回表找行,這避免了大量的單行訪問。

總的來說,編寫查詢語句時應該儘可能選擇合適的索引避免單行查找,儘快能使用數據原生順序從而避免額外的排序操作,並儘可能使用索引覆蓋查詢。

理解索引是如何工作的非常重要,應該根據這些理解創建最合適的索引,而不是根據一些諸如“在多列索引中將選擇性最高的列放在第一列”或“應該爲where子句中出現的所有列創建索引”之類的經驗法則及其推論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章