MySQL之高性能的索引策略(1)

1、索引列必須單獨

讓咱們先看看下面的這段MySQL代碼:mysql

mysql> SELECT id FROM user WHERE id + 1 = 2;

咱們會發現,在上面這段SQL查詢語句中,咱們給的條件是一個有變量的表達式,若是咱們此時的id列上是存在索引的,那上面的語句能不能使用到索引呢?
答案是不能的,由於MySQL沒法自動的解析 id + 1 = 2 這個條件語句,儘管咱們能夠一眼的看出來此時等價於 id = 1,可是這種作法是沒法使用到索引的,所以咱們在查詢的時候,應該使得索引列不能是表達式的一部分,也不能是函數的參數。程序員

2、前綴索引和索引選擇性

若是咱們須要在某一列,例如存放url的一列數據上添加索引來加快查詢的速度,咱們先看看url數據的特色,長,相似的還有TEXT類型的數據等,這些都是一些很長,佔據很大空間的數據,並且會使得對應的索引大且慢。這時候咱們可使用一些優化的索引策略,例如前綴索引。前綴索引與通常的索引不一樣,他在查詢的時候並不會比對該列數據的全部值,而只是比對它的前面的一部分數據。這樣會使得索引變得更加靈活有效率,可是卻下降了索引選擇性
什麼是索引選擇性呢?咱們給定一個公示:
索引選擇性 = 不一樣的索引值 / 數據表的記錄總數
首先思考,爲何會有所謂的不一樣索引值和相同的索引值?這都要創建在咱們是使用前綴索引這種方式創建索引的基礎上。例若有兩個數據,「abcalkjsdhgasdfasdf」「abcalaasdasdqwe」。很顯然這兩個數據是大相徑庭的,可是若是咱們規定前綴索引的長度是數據的前五個字符,那麼會發現這兩個數據的索引值都是「abcal」,即這兩個數據的索引值是同樣的。所以也就下降了索引選擇性。簡單來講,索引選擇性越高,咱們經過索引值能查找到惟一的數據的可能性就越大,索引選擇性越低,咱們經過索引值能查找到的惟一的數據的可能性就越小。那麼這是否就意味着前綴索引是一個不好的選擇呢?並不,由於通常狀況下使用恰當的前綴索引,也是能夠準確的進行數據的查詢,而且可以節省空間的,並且對於BLOB,TEXT或者很長的VARCHAR類型的列,必須使用前綴索引,由於MySQL不容許索引這些列的完整長度。sql

3、多列索引

首先,若是一個數據表有3個列,那麼咱們爲每個列都單獨的建立一個索引,是否是就可以使得在查詢的時候,不管進行怎樣的查找,咱們都能得到最快的效率呢?進行下面的表格創建語句:函數

mysql> CREATE TABLE temp (c1 INTEGER,c2 INTEGER,KEY (c1),KEY (c2));

事實證實,在實際的操做中,這種爲每個列都創建一個索引的「單純」的想法,對查找的效率提高很是的有限,與最佳的索引方案每每效率差距了幾個量級。
在MySQL5.0以後的版本多出了「索引合併」的策略,必定程度上是幫程序員優化了這種在一個數據表上建立許多單列索引的操做,可是仍是不建議使用這種索引策略。在MySQL5.0以前的版本,若是咱們爲表film_actor的字段film_id和actor_id分別建立一個單列索引,而後執行如下的查詢操做:性能

mysql> SELECT film_id, actor_id FROM film_actor
       WHERE  film_id  = 1 OR actor_id = 1;

事實上,在這個查詢語句中,咱們所定義的兩個單列索引都沒法幫忙提升效率,甚至於在老版本的MySQL中,將會使用全表掃描來完成這個查詢,這就使得這個索引策略變得徹底沒有意義。
除非將上面的語句改寫成如下的形式:優化

mysql> SELECT actor_id, film_id FROM film_actor WHERE actor_id = 1
       UNION ALL
       SELECT actor_id, film_id FROM film_actor WHERE film_id = 1

即將查詢改寫成兩個查詢的交集,即每一個查詢都只是用一個列做爲判斷的條件,拿着整MySQL就會很天然的運用這個列的索引。url

所以咱們仍是建議在須要在多個列上創建索引的時候不要單獨的給每一個列創建一個索引,而是選擇創建一個多列索引。spa

mysql> CREATE TABLE temp (c1 INTEGER,c2 INTEGER,KEY (c1, c2));

這個時候選擇一個合適的索引列順序就顯得很是重要了,由於咱們知道,若是咱們使用默認的B-Tree創建一個多列索引的話,MySQL會按照咱們建立時候指定的順序創建索引,即先排c1,再在c1排列好的基礎上排列c2。
並且查詢的時候每次都是從左開始掃描,意味着若是你第一個查詢的列並非索引的最左列,那這個索引對於你來講就形同虛設。
咱們看看以下的一個查詢語句:指針

mysql> SELECT * FROM payment WHERE staff_id = 2 AND customer_id = 584;

那咱們是應該創建一個(staff_id, customer_id)的多列索引仍是將他們的順序顛倒過來呢?咱們能夠先看看這兩個條件的數據量有多大:code

sum(staff_id = 2) = 7992
sum(customer_id = 584) = 30

根據經驗,咱們應該將索引列customer_id 放到前面,由於對應條件值的數據量更小。所以如何選擇索引列的順序仍是應該根據具體的狀況來肯定,沒有惟一的準則。

4、聚簇索引(介紹)

聚簇索引其實只是一種特殊的B-Tree索引,他並非一種區別於其餘索引的單獨的索引形式,而是一種存儲方式。
當使用聚簇索引的時候,全部的數據都存儲在索引樹的葉子節點上。
下圖展現了聚簇索引中的記錄是如何存放的:
image.png
在建立聚簇索引時,InnoDB使用主鍵做爲索引列彙集數據。若是數據表沒有定義主鍵,則會選擇一個惟一的非空索引代替,若是沒有這樣的索引InnoDB則會隱式定義一個主鍵來做爲聚簇索引。
聚簇索引有如下的優勢:

  • 能夠吧相關的數據保存在一塊兒。例如實現電子郵箱時,能夠根據用戶的id來彙集數據,這樣只須要從磁盤讀取少許的數據頁就能獲取某個用戶的所有郵件,若是沒有使用聚簇索引,那麼郵件的排列就是混亂的,有可能每封郵件都要致使一次磁盤IO。
  • 數據訪問更快。聚簇索引將索引和數據保存在同一個B-Tree中(普通索引只保存索引和指向數據行的指針,不保存數據),所以聚簇索引的查詢會更快。
  • 使用覆蓋索引掃描的查詢能夠直接使用頁節點中的主鍵值。

同時,聚簇索引也有如下的缺陷:

  • 聚簇索引最大限度地提升了IO密集型應用的性能,但若是數據所有存放在內存中,則訪問的順序就沒有那麼重要了,聚簇索引就沒有什麼優點了。
  • 插入速度嚴重依賴插入順序。
  • 更新聚簇索引列的代價很高。
  • 基於聚簇索引的表在插入新行,或者主鍵被更新致使須要移動行的時候,可能面臨「頁分裂」的問題。當行的主鍵要求必須將這一行插入到某個已滿的頁面時,存儲引擎會將該頁分裂成兩個頁面容納該行,頁分裂將會卻是表佔據更多的磁盤空間。
  • 聚簇索引可能致使全表掃描變慢,特別是在行比較稀疏的時候,或者因爲頁分裂致使存儲不連續的時候。
  • 二級索引(非聚簇索引)可能比想象中的大沒由於耳機索引的葉子節點包含了引用行的主鍵列。
  • 二級索引訪問須要兩次索引查找,而不是一次。

因爲聚簇索引的內容比較多,會專門出一篇來較爲深刻的將聚簇索引。

相關文章
相關標籤/搜索