高性能索引策略(上)

 高效的選擇和使用索引有很多方式,其中有些事針對特殊案例的優化方法,有些則是針對特定行爲的優化,使用哪個索引,以及如何評估選擇不同索引性能影響的技巧,則需要持續不斷的練習。接下來將介紹如何高效的使用索引。

  獨立的列

  我們通常會看到一些查詢不當的使用索引,或者是的MySQL無法使用已有的索引。如果查詢中的列不是獨立的,則MySQL就不會使用索引。“獨立的列”是指索引列不能是表達式的一部分,也不能是函數的參數。

  例如,下面的這個查詢無法使用actor_id的索引:

  SELECT actor_id FROM actor WHERE actor_id +1 = 5;

  憑肉眼很容易看出WHERE 中的表達式其實等價於 actor_id=4 ,但是MySQL無法自動解析這個方程式。這完全是用戶的行爲。我們應該養成簡化WHERE條件的習慣,始終將索引列單獨放在比較符合的一側。

  下面是常見的錯誤:

  SELECT ... WHERE TO_DAYS(CURRENT_DATE) - TO_DAYS(date_col) <=10;

  前綴索引和索引選擇性

  有時候需要索引很長的字符列,這會讓索引編的大且慢。一個策略是前面提到的模擬hash索引。蛋有時候這樣做還不夠,還可以做什麼呢?

  通常可以索引開始的部分字符串,這樣可以大大節約索引空間,從而提高索引效率。但是這樣也會降低索引的選擇性。索引的選擇性是指,不重複索引的值(也稱爲基數)和數據表的記錄總數(#T)的比值,範圍從1/#T到1之間。索引的選擇性越高,則查詢效率越高,因爲選擇性高的索引可以讓MySQL在查找時過濾掉更多的行。唯一索引的選擇性是1,這是最好的索引選擇性,性能也是最好的。

  一般情況下某個列前綴索引的選擇性也是足夠高的,足以滿足查詢性能。對於BLOB、TEXT或者很長的VARCHAR類型的列,必須使用前綴索引,因爲MySQL不允許索引這些列的完整長度。

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

  爲了決定前綴的合適長度,需要找到最常見的值的列表,然後和最常見的前綴列進行比較。

  前綴索引是一種能使索引更小,更快的有效辦法,但另一方面也有其缺點:MySQL無法使用前綴索引做order by 和group by 操作,也無法使用前綴索引做覆蓋掃描。

  一個常見的場景是針對很懂行的十六進制唯一ID使用前綴索引。在前面已經討論了很多有效的基數來存儲這類ID信息,但如果使用打包過的解決方案,因而無法修改存儲結構,那該怎麼辦?此時如果採用長度爲8的淺醉索引通常能夠顯著的提升性能,並且這種方法對引用層上完全透明。

  有時候後綴索引也有用途(例如,找到摸個域名的所有電子郵件地址)。MySQL原生並不支持反向索引,但是可以吧字符串反轉後存儲,並給予此建立前綴索引。可以通過觸發器來維護這種索引。

  多列索引

  很多人對多列索引的理解都不夠。一個常見的錯誤就是,爲每個列建立獨立的索引,或者按照錯誤的順序創建多列索引。

  我們會在稍後的章節中單獨討論索引列的順序問題。先來看第一個問題,爲每個列牀架獨立的索引,從SHOW CREATE TABLE 中很容易看到這種情況:

  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 條件,這兩個單列索引都不是好的選擇:

  SELECT film_id ,actor_id FROM film_actor WHERE actor_id=1 or film_id =1;

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

  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;

  但是在MySQL5.0 和更高的版本中,產線能夠同時使用者兩個單列索引進行掃米昂,並將結果進行合併。這種算法有三個變種:OR條件的聯合,AND條件的相交,組合前面兩種情況的聯合及相交。下面的查詢就是使用了兩個索引掃描的聯合,通過EXPLAIN中的Extra列可以看出這點:

  EXPLAIN SELECT film_id,actor_id FROM film_actor WHERE actor_id=1 or film_id = 1 \G

  MySQL會使用這類技術優化負責的查詢,所以在某些語句的EXTRA列中還可以看到嵌套操作。

  索引合併策略有時候是一種優化的結構,但實際上更多的時候說明了表上的索引建的很糟糕:

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

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

  更重要的是,優化器不會吧這些成本算到“查詢成本”中,遊虎丘只關心隨機頁面讀取。這會使得查詢成本被低估,導致該執行計劃還不如直接走全表掃描。這樣做不但會消耗更多的cup和內存資源,還可能影響查詢的併發性,但如果是單獨魚腥這樣的查詢則往往會忽略對並發現的影響。通常來說,還不弱在MySQL4.1或更早的時代一樣,將查詢改寫成UNION的方式往往會更好。


原文:http://www.cnblogs.com/zhengyanqiu/p/4979441.html

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