1. 程式人生 > 實用技巧 >【mysql】- 索引簡介篇

【mysql】- 索引簡介篇

簡介

  • 我們都知道mysql使用儲存引擎的是InnoDB,InnoDB使用的索引的對應的資料結構是B+

結構圖:

  • 如上圖所示,我們實際使用者記錄是存放在B+樹的最底層的節點上,這些節點也被稱為葉子節點或者葉節點,其餘用了存放目錄項的節點稱為非葉子節點或者內節點,最上邊的節點為根節點
  • InnoDB是使使用來作為管理理儲存空間的基本單位,也就是最多能保證16KB的連續儲存空間,而隨著表中記錄數量量的增多,需要非常大的連續的儲存空間才能把所有的目錄項都放下,這對記錄數量非常多的表是不現實的
  • 目錄項兩個列是主鍵頁號,與使用者記錄差不多,為了和使用者記錄進行區分,我們把這些用於表示目錄項的記錄稱為目錄項記錄
    ,區分方法為:
    • 通過記錄頭資訊的record_type屬性:
      • 0:普通使用者記錄
      • 1:目錄項記錄
      • 2:最小記錄
      • 3:最大記錄
  • 其設計者規定了最下邊的那層,也就是存放記錄的那層為第0層,之後依次往上加。
  • 現在以查詢主鍵為20的記錄為例,採用二分法進行資料查詢
    • 首先到儲存目錄項記錄的頁,也就是頁33中通過二分法快速定位到對應的目錄項,因為1<20<320,所以定位到對應記錄所在的頁就是30
    • 此時12<20<209,所以定位到對應的記錄所在的頁就是頁9;
    • 再到儲存使用者記錄的頁9中根據二分法快速定位到主鍵值為20的使用者記錄。

擴充套件,為什麼不用B樹,而採用B+樹呢?

  • 根據B樹的特點是每個節點都是儲存記錄的,那麼就會存在一個問題,假如同樣的一個深度的樹,B樹會比B+樹的頁要大很多,同樣一頁16k的資料,載入B+樹的記錄的範圍會比B樹的範圍要大,那麼磁碟IO的操作次數顯然是B樹要更多一些。

索引的分類

  • 聚簇索引
    • 使用用記錄主鍵值的大小進行記錄和頁的排序,這包括三個方面的含義:
      • 頁內的記錄是按照主鍵的大小順序排成一個單向連結串列。
      • 各個存放使用者記錄的頁也是根據頁中使用者記錄的主鍵大小順序排成一個雙向連結串列。
      • 存放目錄項記錄的頁分為不不同的層次,在同一層次中的頁也是根據頁中目錄項記錄的主鍵大小順序排成一個雙向連結串列。
    • B+樹的葉子節點儲存的是完整的使用者記錄。

大家有木有發現,上邊介紹的聚簇索引只能在搜尋條件是主鍵值時才能發揮作用,因為B+樹中的資料都是按照主鍵進行行排序的。那如果我們想以別的列列作為搜尋條件該咋辦呢?難道只能從頭到尾沿著連結串列依次遍歷記錄麼?此時便有了二級索引

  • 二級索引(輔助索引)

  • 如圖
    • 我們可以多建幾棵B+樹,不不同的B+樹中的資料採用不不同的排序規則。
    • 這個B+樹與上邊介紹的聚簇索引有幾處不不同:
      • 使用記錄c2列的大小進行行記錄和頁的排序,這包括三個方面的含義:
        • 頁內的記錄是按照c2列的大小順序排成一個單向連結串列。
        • 各個存放使用者記錄的頁也是根據頁中記錄的c2列大小順序排成一個雙向連結串列。
        • 存放目錄項記錄的頁分為不不同的層次,在同一層次中的頁也是根據頁中目錄項記錄的c2列大小順序排成一個雙向連結串列。
      • B+樹的葉子節點儲存的並不不是完整的使用者記錄,而只是c2列 +主鍵這兩個列列的值。
      • 目錄項記錄中不不再是主鍵+頁號的搭配,而變成了了c2列+頁號的搭配。
    • 以查詢c2列的值為4的記錄為例,查詢過程如下:
      • 確定目錄項記錄
        • 根據根頁面,也就是頁44,可以快速定位到目錄項記錄所在的頁為頁42(因為 2 <4 < 9 )。
      • 通過目錄項記錄頁確定使用者記錄真實所在的頁。
        • 頁42中可以快速定位到實際儲存使用者記錄的頁,但是由於c2列列並沒有唯一性約束,所以c2列列值為4的記錄可能分佈在多個數據頁中,又因為2 < 4 ≤ 4,所以確定實際儲存使用者記錄的頁在頁34頁35中。
      • 在真實儲存使用者記錄的頁中定位到具體的記錄。
        • 頁34頁3中定位到具體的記錄。
      • 但是這個B+樹的葉子節點中的記錄只儲存了c2c1(也就是主鍵)兩個列,所以我們必須再根據主鍵值去聚簇索引中再查詢一遍完整的使用者記錄。

上面的操作其實就是一個回表,整個完成的查詢需要用到2棵B+樹。那為什麼需要回表操作呢?直接把完整的使用者記錄放到葉子節點 不不就好了了麼?

  • 原因:
    • 太佔用地方,相當於每建立一棵B+樹都需要把所有的使用者記錄再都拷貝一遍,這就有點太浪費儲存空間了了
  • 聯合索引

    • 我們也可以同時以多個列的大小作為排序規則,也就是同時為多個列列建立索引,比方說我們想讓B+樹按照c2c3列的大小進行排序,這個包含兩層含義:

      • 先把各個記錄和頁按照c2列進行排序。
      • 在記錄的c2列列相同的情況下,採用c3列進行排序。

      示意圖如下:

    • 如圖所示,我們需要注意一下幾點:

      • 每條目錄項記錄都由c2c3頁號這三個部分組成,各條記錄先按照c2列列的值進行排序,如果記錄的c2列相同,則按照c3列的值進行排序。
      • B+樹葉子節點處的使用者記錄由c2c3和主鍵c1列組成。
    • 千萬要注意一點,以c2c3列的大小為排序規則建立的B+樹稱為聯合索引,它的意思與分別為c2c3列分別建立索引的表述是不不同的,不不同點如下:

      • 建立聯合索引只會建立如上圖一樣的1棵B+樹。
      • c2c3列分別建立索引會分別以c2c3列的大小為排序規則建立2棵B+樹。
  • 總結:

    • 1.對於InnoDB儲存引擎來說,在單個頁中查詢某條記錄分為兩種情況:
      • 以主鍵為搜尋條件,可以使用Page Directory通過二分法快速定位相應的使用者記錄。
      • 以其他列列為搜尋條件,需要按照記錄組成的單鏈表依次遍歷各條記錄。
    • 2.沒有索引的情況下,不不論是以主鍵還是其他列列作為搜尋條件,只能沿著頁的雙鏈表從左到右依次遍歷各個頁。
    • 3.InnoDB儲存引擎的索引是一棵B+樹,完整的使用者記錄都儲存在B+樹第0層的葉子節點,其他層次的節點都屬於內節點 ,內節點里儲存的是目錄項記錄 。 InnoDB 的索引分為兩大種:
      • 聚簇索引
        • 以主鍵值的大小為頁和記錄的排序規則,在葉子節點處儲存的記錄包含了表中所有的列。
      • 二級索引
        • 以自定義的列的大小為頁和記錄的排序規則,在葉子節點處儲存的記錄內容是+主鍵
    • 4.MyISAM儲存引擎的資料和索引分開儲存,這種儲存引擎的索引全部都是 ⼆二級索引 ,在葉子節點處儲存的是+頁號