轉自:http://www.cnblogs.com/yuyue2014/p/3662005.htmlhtml
1. MySQL索引實現算法
在MySQL中,索引屬於存儲引擎級別的概念,不一樣存儲引擎對索引的實現方式是不一樣的,下面主要討論MyISAM和InnoDB兩個存儲引擎的索引實現方式。緩存
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域的值爲地址,讀取相應數據記錄。spa
MyISAM的索引方式也叫作「非彙集」的,之因此這麼稱呼是爲了與InnoDB的彙集索引區分。3d
雖然InnoDB也使用B+Tree做爲索引結構,但具體實現方式卻與MyISAM大相徑庭。code
第一個重大區別是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.1 獨立的列
「獨立的列」是指索引列不能是表達式的一部分,也不能是函數的參數。
錯誤的寫法:
select id from tab where id+1=5;
select id,value from tab where to_days(now())-to_days(gmt_created) <=10;
應該養成簡化where條件的習慣,始終將索引列單獨放在比較符號的一側。
正確的寫法:
select id,value from tab where gmt_created >= DATA_SUB(now(),interval 10 day );
2.2 索引選擇性
selectivity = distinct Values / total Rows
索引的選擇性是指,不重複的索引值和數據表的記錄總數的比值。索引的選擇性越高則查詢效率越高,由於選擇性高的索引可讓MySQL在查找時過濾掉更多的行。
惟一索引的選擇性是1,這是最好的索引選擇性,性能也是最好的。
2.3 前綴索引
對於BLOB、TEXT或者很長的VARCHAR類型的列,必須使用前綴索引,由於MySQL不容許索引這些列的完整長度。
訣竅在於要選擇足夠長的前綴以保持較高的選擇性,同時又不能太長(以便節約空間)。前綴應該足夠長,以使得前綴索引的選擇性接近於索引整個列。
2.4 多列索引
一個常見的錯誤是,爲每一個列建立獨立的索引,或者按照錯誤的順序建立多列索引。
錯誤的寫法:
create table t ( c1 int, c2 int, c3 int, key(c1), key(c2), key(c3) );
在多個列上創建獨立的單列索引大部分狀況下並不能提升MySQL的查詢性能。對於下面的查詢where條件,這兩個單列索引都是很差的選擇:
select film_id, actor_id from table1 where actor_id=1 or film_id=1;
在老的MySQL版本中,MySQL會對這個查詢使用全表掃描。除非改寫成兩個查詢UNION的方式。
select film_id, actor_id from table1 where actor_id=1 union all select film_id, actor_id from table1 where film_id=1 and actor_id<>1;
MySQL5.0和更新的版本引入了一種叫「索引合併」的策略,查詢可以同時使用這兩個單列索引進行掃描,並將結果合併。這種算法有三個變種:OR條件的聯合(union),AND條件的相交(intersection),組合前兩種狀況的聯合及相交。索引合併策略有時候是一種優化的結果,但實際上更多時候說明了表上的索引建得很糟糕:
(1)當出現服務器對多個索引作相交操做時(多個AND條件),一般意味着須要一個包含全部相關列的多列索引,而不是多個獨立的單列索引。
(2)當出現服務器對多個索引作聯合操做時(多個OR條件),一般須要耗費大量的CPU和內存資源在算法的緩存、排序和合並操做上。特別是當其中有些索引的選擇性不高,須要合併掃描返回的大量數據的時候。
(3)若是在explain中看到有索引合併,應該好好檢查一下查詢和表的結構,看是否是已是最優的。
2.5 覆蓋索引
若是一個索引包含(或者說覆蓋)全部須要查詢的字段的值,就稱之爲「覆蓋索引」。MySQL利用索引返回select列表中的字段,而沒必要根據索引再次回表讀取數據頁。
select id,status from tab where id=2 alter table add key ind_t_id_status (id,status);
不是全部類型的索引均可以成爲覆蓋索引。覆蓋索引必需要存儲索引列的值,而哈希索引、空間索引和全文索引等都不存儲索引列的值,因此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子句中出現的全部列建立索引」之類的經驗法則及其推論。