1. 程式人生 > >MySQL——關於索引的總結

MySQL——關於索引的總結

索引的優缺點

  首先說說索引的優點:最大的好處無疑就算提高查詢效率。有的索引還能保證資料的唯一性,比如唯一索引。

  而它的壞處很明顯:索引也是檔案,我們在建立索引時,也會建立額外的檔案,所以會佔用一些硬碟空間。其次,索引也需要維護,我們在增加刪除資料的時候,索引也需要去變化維護。當一個表的索引多了以後,資源消耗是很大的,所以必須結合實際業務再去確定給哪些列加索引。

索引的結構

  再說說索引的基本結構。一說到這裡肯定會脫口而出:B+樹!瞭解B+樹前先要了解二叉查詢樹和二叉平衡樹。二叉查詢樹:左節點比父節點小,右節點比父節點大,所以二叉查詢樹的中序遍歷就是樹的各個節點從小到大的排序。二叉平衡樹:左右子樹高度差不能大於1。B+樹就是結合了它們的特點,當然,不一定是二叉樹。

  為什麼要有二叉查詢樹的特點??因為查詢效率快,二分查詢在這種結構下,查詢效率是很快的。那為什麼要有平衡樹的特點呢?試想,如果不維護一顆樹的平衡性,當插入一些資料後,樹的形態有可能變得很極端,比如左子樹一個數據沒有,而全在右子樹上,這種情況下,二分查詢和遍歷有什麼區別呢?而就是因為這些特點需要去維護,所以就有了上面提到的缺點,當索引很多後,反而增加了系統的負擔。

  接著說B+樹。它的結構如下:

  可以發現,葉子節點其實是一個雙向迴圈連結串列,這種結構的好處就是,在範圍查詢的時候,我只用找到一個數據,就可以直接返回剩餘的資料了。比如找小於30的,只用找到30,其餘的直接通過葉子節點間的指標就可以找到。再說說其他特點:資料只存在於葉子節點。當葉子節點滿了,如果再新增資料,就會拆分葉子節點,父節點就多了個子節點。如果父節點的位置也滿了,就會擴充高度,就是拆分父節點,如25 50 75拆分成:25為左子樹,75為右子樹,50變成新的頭節點,此時B+樹的高度變成了3。它們的擴充的規律如下表,Leaf Page是葉子節點,index Page是非葉子節點。

  再說說B樹,B樹相比較B+樹,它所有節點都存放資料,所以在查詢資料時,B樹有可能沒到達葉子節點就結束了。再者,B樹的葉子節點間不存在指標。

  最後說說Hash索引,相較於B+樹,Hash索引最大的優點就是查詢資料快。但是Hash索引最大的問題就是不支援範圍查詢。試想,如果查詢小於30的資料,hash函式是根據資料的值找到其對應的位置,誰又知道小於30的有哪幾個資料。而B+樹正好相反,範圍查詢是它的強項。

  附錄:Hash到底是啥??雜湊中文名雜湊,雜湊只是它的音譯。為啥都說Hash快??首先有一塊雜湊表(散列表),它的資料結構是個陣列,一個任意長度的資料通過hash函式都可以變成一個固定長度的資料,叫hash值。然後通過hash值確定在陣列中的位置,相同資料的hash值是相同的,所以我們儲存一個數據以後,只需O(1)的時間複雜度就可以找到資料。那hash函式又是啥??算術運算或位運算,很多應用裡都有hash函式,但實際運算過程大不一樣。這是Java裡String的hashCode方法:

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

  還有一個問題,hash函式計算出來的hash值有可能存在碰撞,即兩個不同的資料可能存在相同的hash值,在MySQL或其他的應用中,如Java的HashMap等,如果存在碰撞就會以當前陣列位置為頭節點,轉變成一個連結串列。

  說到這裡也清楚了為啥Java中引用型別要同時重寫hashCode和equals了。兩個物件,例項就算一模一樣,它們的hash值也不相等,為啥不相等??預設的Object的hashCode方法會根據物件來計算hash值的,例項相同,但它們還是兩個不同的物件啊,所以我們重寫hashCode時,最簡單的方法就是呼叫Object的hashCode方法,然後傳入該引用型別的屬性,讓hashCode方法只根據這幾個屬性來計算,那麼例項相同的話,它們的hash值也會相等。等hashCode比較完後,如果相等再比較例項內容,也就是equals,確保不是hash碰撞。

索引的分類

  主鍵索引:如果我們指定了一個主鍵,那麼這個主鍵就是主鍵索引。如果我們沒有指定,Mysql就會自動找一個非空的唯一索引當主鍵。如果沒有這種欄位,Mysql就會建立一個大小為6位元組的自增主鍵。如果有多個非空的唯一索引,那麼就讓第一個定義為唯一索引的欄位當主鍵,注意,是第一個定義,而不是建表時出現在前面的。

  輔助索引:對於輔助索引來說,它們的B+樹結構稍微有點特殊,它們的葉子節點儲存的是主鍵,而不是整個資料。所以在大部分情況下,使用輔助索引查詢資料,需要二次查詢。但並不是所有情況都需要二次查詢。比如查詢的資料正好就是當前索引欄位的值,那麼直接返回就行。這裡提一句,B+樹的key就是對應索引欄位的內容。

  而輔助索引又有一些分類:唯一索引:不能出現重複的值,也算一種約束。普通索引:可以重複、可以為空,一般就是查詢時用到。字首索引:只適用於字串型別資料,對字串前幾個字元建立索引。全文索引:作用是檢測大文字資料中某個關鍵字,這也是搜尋引擎的一種技術。

 

  聚集索引:注意,聚集索引、非聚集索引和前面幾個索引的分類並不是一個層面上的。上面的幾個分類是從索引的作用來分析的。聚集、非聚集索引是從索引檔案上區分的。主鍵索引就屬於聚集索引,即索引和資料存放在一起,葉子節點存放的就是資料。資料表的.idb檔案就是存放該表的索引和資料。

  非聚集索引:輔助索引屬於非聚集索引,說到這也就明白了。索引和資料不存放在一起的就是非聚集索引。在MYISAM引擎中,資料表的.MYI檔案包含了表的索引, 該表的 葉子節點儲存索引和索引對應資料的指標,指向.MYD檔案的資料。

索引的幾點使用經驗

  適合建立索引的欄位:經常被查詢的欄位;經常作為條件查詢的欄位;經常用於外來鍵連線或普通的連表查詢時進行相等比較欄位;不為null的欄位;如果是多條件查詢,最好建立聯合索引,因為聯合索引只有一個索引檔案。

  不適合建立索引的欄位:經常被更新的欄位、不經常被查詢的欄位、存在相同功能的