一:索引的類型mysql
二:索引的優勢sql
三:高性能索引策略bash
四:索引案例服務器
1.1類型介紹數據結構
索引有不少類型,能夠爲不一樣場景提供更好的性能。在MySQL中,索引是在存儲引擎層而不是服務器層實現的。因此,並無統一的索引標準:不一樣存儲引擎的索引的工做方式並不同,也不是全部的存儲引擎都支持全部類型的索引,即便多個存儲引擎支持同一種類型的索引,其底層的實現也可能不一樣ide
1.2 B+Tree索引函數
存儲引擎以不一樣的方式使用B+Tree索引,性能也各有不一樣,各有優劣。性能
例如MyISAM使用前綴壓縮技術使得索引更小,但InnoDB則按照原數據格式進行存儲。優化
再如MyISAM索引經過數據的物理位置引用被索引行,而InooDB則根據主鍵引用被索引的行spa
1.2.1 B+Tree索引數據結構
1.2.2理解數據結構
假若有以下數據表:
CREATE TABLE people( last_name varchar(50) not null, first_name varchar(50) not null, dob date not null, gender eum('m','f') not null, key(last_name, first_name, dob) )
對於每一行的數據,索引中包含了,last_name,first_name和dob列的值
注意:索引對多個值進行排序的依據是CREATE TABLE語句中定義索引時列的順序。看最後兩個條目,兩我的的姓和名都同樣,則根據他們的出生日期來排列順序
1.2.3 B+Tree索引對以下類型的查詢有效
全值匹配:和索引中的全部列進行匹配,例如前面建立的索引可用於查找姓名爲Allen Cuba,出生於1960-01-01的人
匹配最左前綴:前面建立的索引可用於查找全部姓爲Allen的人,即只使用索引的第一列
匹配列前綴:前面建立的索引也能夠只匹配某一列的值的開頭部分,例如能夠用於查找全部以J開頭的姓的人
匹配範圍值:前面建立的索引可用於查找姓在Allen和Barrymore之間的人。(這裏只使用了索引的第一列)
精確匹配某一列並範圍匹配另一列:前面建立的索引可用於查找全部姓爲Allen,而且名字是字母K開頭(好比Kim)的人。即第一列last_name全匹配,第二列first_name範圍匹配
只訪問索引的查詢:B+Tree一般能夠支持「只訪問索引的查詢」,即查詢只須要訪問索引,而無須訪問數據行。
由於索引樹中的節點是有序的,因此除了按值查找以外,索引還能夠用於查詢中的ORDER BY操做。通常來講,若是B+Tree能夠按照某種方式查找到值,那麼也能夠按照這種方式用於排序。因此,若是ORDER BY 子句知足前面列出的幾種查詢類型,則這個索引也能夠知足對應的排序需求
1.2.4 B+Tree索引的限制
1.2.4.1若是不是按照索引的最左列開始查找,則沒法使用索引。例如上面例子中的索引沒法用於查找名字爲Kim的人,也沒法查找某個特定生日的人,由於這兩列都不是最左數據列。相似的,也沒法查找姓氏以某個字母結尾的人
1.2.4.2不能跳過索引中的列。也就是說,前面所述的索引沒法用於查找姓爲Allen而且在某個特定日期出生的人。若是不指定名(first_name),則MySQL只能使用索引的第一列
1.2.4.3若是查詢中有某個列的範圍查詢,則其右邊全部列都沒法使用索引優化查找。例若有查詢WHERE last_name='Allen' ANDfirst_name LIKE 'K%' AND dob='1930-07-12',這個查詢只能使用索引的前兩列,由於這裏LIKE是一個範圍條件(可是服務器能夠把其他列用於其它目的)。若是範圍查詢列值數量有限,那麼能夠經過使用多個等於條件來代替範圍條件。儘量將範圍查詢放到最後,由於範圍查詢後的列不能使用索引
2.1索引的優勢
索引可讓服務器快速的定位到表的指定位置。可是這並非索引的惟一做用,到目前爲止
能夠看到,根據建立索引的數據結構不一樣,索引也有一些其它的附加做用
最多見的B+Tree索引,按照順序存儲數據,因此MySQL能夠用來作ORDER BY 和GROUPBY操做。由於數據是有序的,因此B+Tree也就會將相關的列值都存儲在一塊兒。最後,由於索引中存儲了實際的列值,因此某些查詢只使用索引就可以完成所有查詢。據此特性,總結下來索引有以下三個優勢:
1.索引大大減小了服務器須要掃描的數據量
2.索引能夠幫助服務器避免排序和臨時表
3.索引能夠將隨機I/O變爲順序I/O
Lahdenmaki和Leach提出,如何評價一個索引是否適合某個查詢的「三星系統」:
A.索引將相關的記錄放到一塊兒則得到一星;
B.若是索引中的數據順序和查找中的排序順序一致則得到兩星;
C.若是索引中的列包含了查詢中須要的所有列則得到三星;
3.高性能的索引策略
正確的建立索引和使用索引是實現高性能查詢的基礎。如今討論一下如何真正的發揮這些索引的優點
3.1獨立的索引
一般看到一些查詢不當的使用索引,或者使MySQL沒法使用已有的索引。若是查詢中的列不是獨立的,則MySQL就不會使用索引。「獨立的列」是指索引列不能是表達式的一部分,也不能是函數的參數
例1:下面這個查詢就沒法使用user_id列的索引
mysql> SELECT user_id FROM tb1 WHERE user_id+1 = 5;
很容易看出WHERE中的表達式起始等價於user_id=4,可是MySQL沒法自動解析這個方程式,這徹底是用戶行爲。咱們應該養成簡化WHERE條件的習慣,始終將索引列單獨放在比較符號的一側
例2:下面這個查詢就沒法使用date_col列的索引
mysql> SELECT ... WHERE TO_DAYS(CUURRENT_DATE) - TO_DAYS(date_col) <= 10
解決方法:在程序中獲取當前時間,減去10天,加入最後的值存儲在last_date_val中
mysql> SELECT ... WHERE date_col <= last_date_val
3.2前綴索引和索引選擇性
有時候須要索引很長的字符列,這會讓索引變得很慢。一般能夠索引開始的部分字符,這樣能夠大大節約索引空間,從而提升索引效率。但這樣也會下降索引的選擇性。
索引選擇性是指:不重複的索引值(也稱爲基數)和數據表的記錄總數(#T)的比值,範圍從1/#T到1之間。索引的選擇性越高,則查詢效率越高,由於選擇性高的索引可讓MySQL在查找時過濾掉更多的行。惟一索引的選擇性是1,這是最好的索引選擇性,性能也最好
理解索引選擇:每行記錄都有一個索引值,若是前綴過少,那麼索引值重複的可能性就越大,這樣在查詢的時候查詢到的數據就越多,過濾掉的數據就越少,因此要找一個合理的前綴。
合理前綴的訣竅在於:選擇足夠長的前綴以保證較高的選擇性,同時又不能太長(以便節約空間)。前綴應該足夠長,以使得前綴索引的選擇性接近於索引整個列。換句話說,前綴的「基數」應該接近於完整列的「基數」
3.2.1如何選擇合理前綴的長度
mysql> CREATE TABLE tb( -> city VARCHAR(50) NOT NULL -> ); Query OK, 0 rows affected (0.26 sec) mysql> SELECT COUNT(*) AS cnt, city_name FROM city GROUP BY city_name ORDER BY cnt DESC LIMIT 10;
能夠看到,上面每一個值都出現了5-7次,如今查找到最頻繁出現的城市,先從3個前綴字母開始
mysql> SELECT COUNT(*) AS cnt, LEFT(city_name,3) AS pref FROM city GROUP BY pref ORDER BY cnt DESC LIMIT 10;
每一個前綴都比原來的城市出現的次數更多,所以惟一前綴比惟一城市要少的多。而後增長前綴的長度,知道這個前綴的選擇性接近完整列的選擇性。
mysql> SELECT COUNT(*) AS cnt, LEFT(city_name,7) AS pref FROM city GROUP BY pref ORDER BY cnt DESC LIMIT 10;
計算合適前綴的長度的方法就是計算完整列的選擇性,並使前綴的選擇性接近於完整列的選擇性。下面是如何計算完整列的選擇性:
mysql> SELECT COUNT(DISTINCT city_name)/COUNT(*) FROM city;
一般來講(儘管也有例外狀況),這個例子中若是前綴的選擇性可以接近於0.4317,基本上就可用了,能夠在一個查詢中針對不一樣前綴長度計算。
顯示長度爲7的時候,再增長前綴長度,選擇性已經沒有提高了(或者某些時候提高幅度很小了)
3.2.2建立前綴索引
mysql> ALTER TABLE city ADD [KEY|INDEX] city_name_index(city_name(7));
前綴索引是一種能使索引更小、更快的有效方法,但另外一方面也有其缺點:MySQL沒法使用前綴索引作ORDER BY 和 GROUP BY ,也就沒法使用前綴索引作覆蓋掃描
3.3多列索引
不少人對多列索引的理解還不夠。一個常見的錯誤就是,爲每一個列建立獨立的索引,或者按照錯誤的順序建立多列索引
CREATE TABLE t( c1 INT, c2 INT, c3 INT, KEY(c1), KEY(c2), KEY(c3) );
這種索引策略,通常是因爲人們聽到一些說,「把WHERE條件裏面的列都建上索引」致使的。其實是很是錯誤的,這樣一來最好的狀況下也只能是「一星」索引,其性能比起真正最有的索引可能差幾個數量級。有時候若是沒法設計一個「三星」索引,那麼不如忽略掉WHERE子句,集中精力優化索引列的順序,或者建立一個全覆蓋索引
在多個列上創建獨立的單列索引大部分狀況下並不能提升MySQL的查詢性能。MySQL5.0和更新版本引入了一種叫「索引合併」的策略,必定程度上可使用表上的多個單列索引來定位指定的行。
更早版本的MySQL只能使用其中某一個單列索引,而後這種狀況下沒有哪個獨立的單列索引是很是有效的。例如:表film_actor在字段film_id和actor_id上各有一個單列索引,但對於下面這個查詢WHERE條件,這兩個單列索引都不是好的選擇
mysql> SELECT film_id,actor_id FROM film_actor WHERE actor_id=1OR film_id=1;
在老的MySQL版本中,MySQL對這個查詢會使用全表掃描。除非改寫成以下的兩個查詢。
mysql> SELECT film_id,actor_id FROM film_actor WHERE actor_id=1 -> UNION ALL -> SELECT film_id,actor_id FROM film_actor WHERE film_id=1 AND actor_id<>1;
3.4選擇合適的索引列順序
3.4.1例子分析
mysql> SELECT * FROM payment WHERE staff_id = 2 AND customer_id = 584;
是建立一個(staff_id, customer_id)索引仍是應該顛倒順序呢?能夠作一些查詢來肯定這個表中值的分佈狀況,並肯定哪一個列的選擇性更高。
mysql> SELECT SUM(staff_id=2),SUM(customer_id=584) FROM payment;
mysql> SELECT COUNT(DISTINCT(staff_id))/COUNT(*) AS staff_id , COUNT(DISTINCT(customer_id))/COUNT(*) AS customer_id , COUNT(*) FROM payment;