MySQL索引

問題引入

  1. 查詢時條件字段加索引爲什麼比不加索引快?
  2. 爲什麼MySQL建議使用自增主鍵而非UUID主鍵?
  3. 什麼是二級索引?
  4. 爲什麼建立了聯合索引但只匹配索引後邊的列就無法使用索引?

沒有索引的查找

  例子:SELECT [列名列表] FROM 表名 WHERE 列名 = xxx;

以主鍵爲搜索條件

  定位到記錄所在的頁(如果數據只有一頁,則不需要此步驟),在頁目錄中使用二分法快速定位到對應的槽,然後再遍歷該槽對應分組中的記錄即可快速找到指定的記錄。

以其他列作爲搜索條件

  從第一頁開始查找,每頁中從最小記錄開始依次遍歷單鏈表中的每條記錄,然後對比每條記錄是不是符合搜索條件。

使用索引之後的查找

索引背後的設計
  1. 下一個數據頁中用戶記錄的主鍵值必須大於上一個頁中用戶記錄的主鍵值。在對頁中的記錄進行增刪改操作的過程中,必須通過一些諸如記錄移動的操作來始終保證這個狀態一直成立,當頁滿的時候,就會產生頁分裂。
  2. 數據存儲在頁中,爲所有的頁建立目錄項(目錄其實就是索引了)。
  3. 目錄項的存儲:複用之前存儲用戶記錄的數據頁來存儲目錄項,每個目錄項記錄只存儲主鍵值和對應的頁號,頁滿則進行頁分裂。InnoDB使用記錄頭信息裏的record_type屬性來區分普通的用戶記錄和目錄項記錄
record_type 值含義:
0:普通的用戶記錄
1:目錄項記錄
2:最小記錄
3:最大記錄
  1. 表中的數據非常多則會產生很多存儲目錄項記錄的頁,那我們怎麼根據主鍵值快速定位一個存儲目錄項記錄的頁呢?其實也簡單,爲這些存儲目錄項記錄的頁再生成一個更高級的目錄。
    在這裏插入圖片描述
  2. 上述其實組成了傳說中的B+樹,實際用戶記錄其實都存放在B+樹的最底層的節點上,這些節點也被稱爲葉子節點或葉節點,其餘用來存放目錄項的節點稱爲非葉子節點或者內節點,其中B+樹最上邊的那個節點也稱爲根節點。
聚簇索引

  兩個特點:
  一是使用記錄主鍵值的大小進行記錄和頁的排序,這包括三個方面的含義:

1.頁內的記錄是按照主鍵的大小順序排成一個單向鏈表。
2.各個存放用戶記錄的頁也是根據頁中用戶記錄的主鍵大小順序排成一個雙向鏈表。
3.存放目錄項記錄的頁分爲不同的層次,在同一層次中的頁也是根據頁中目錄項記錄的主鍵大小順序排成一個雙向鏈表。

  二是B+樹的葉子節點存儲的是完整的用戶記錄(包括隱藏列)。

  InnoDB存儲引擎會自動的爲我們創建聚簇索引。在InnoDB存儲引擎中,聚簇索引就是數據的存儲方式(所有的用戶記錄都存儲在了葉子節點),也就是所謂的索引即數據,數據即索引。

查找過程如下:
1. 確定目錄項記錄頁。
2. 通過目錄項記錄頁確定用戶記錄真實所在的頁。
3. 在真實存儲用戶記錄的頁中定位到具體的記錄。
二級索引

  按照其他條件(非主鍵)建立的B+樹(索引)就是二級索引。
  使用記錄索引列的大小進行記錄和頁的排序,這包括三個方面的含義:

1.頁內的記錄是按照索引列的大小順序排成一個單向鏈表。
2.各個存放用戶記錄的頁也是根據頁中記錄的索引列大小順序排成一個雙向鏈表。
3.存放目錄項記錄的頁分爲不同的層次,在同一層次中的頁也是根據頁中目錄項記錄的索引列大小順序排成一個雙向鏈表。

  兩個特點:
  一是B+樹的葉子節點存儲的並不是完整的用戶記錄,而只是索引列+主鍵這兩個列的值。
  二是目錄項記錄中不再是主鍵+頁號的搭配,而變成了索引列+頁號的搭配。

查找過程如下:
1. 確定目錄項記錄頁
2. 通過目錄項記錄頁確定用戶記錄真實所在的頁。
3. 在真實存儲用戶記錄的頁中定位到具體的記錄。
4. 但是這個B+樹的葉子節點中的記錄只存儲了索引列和主鍵兩個列,所以我們必須**再根據主鍵值去聚簇索引中再查找一遍完整的用戶記錄**。這個過程叫做**回表**。

  因爲這種按照非主鍵列建立的B+樹需要一次回表操作纔可以定位到完整的用戶記錄,所以這種B+樹也被稱爲二級索引(英文名secondary index),或者輔助索引。

聯合索引

  按照索引列1和索引列2的大小進行排序,這個包含兩層含義:

1.先把各個記錄和頁按照索引列1進行排序。
2.在記錄的索引列1相同的情況下,再用索引列2進行排序。

注意:
  每條目錄項記錄都由索引列1、索引列2、主鍵(主鍵爲了保證唯一性)、頁號這幾個部分組成,各條記錄先按照索引列1的值進行排序,如果記錄的索引列1相同,則按照索引列2的值進行排序。
  B+樹葉子節點處的用戶記錄由索引列1、索引列2和主鍵組成。

B+樹的形成過程

  1. 每當爲某個表創建一個B+樹索引(聚簇索引不是人爲創建的,默認就有)的時候,都會爲這個索引創建一個根節點頁面。最開始表中沒有數據的時候,每個B+樹索引對應的根節點中既沒有用戶記錄,也沒有目錄項記錄。
  2. 隨後向表中插入用戶記錄時,先把用戶記錄存儲到這個根節點中。
  3. 當根節點中的可用空間用完時繼續插入記錄,此時會將根節點中的所有記錄複製到一個新分配的頁,比如頁a中,然後對這個新頁進行頁分裂的操作,得到另一個新頁,比如頁b。這時新插入的記錄根據鍵值(也就是聚簇索引中的主鍵值,二級索引中對應的索引列的值)的大小就會被分配到頁a或者頁b中,而根節點便升級爲存儲目錄項記錄的頁。

MyISAM中的索引

  索引和數據分開存儲:

  1. 將表中的記錄按照記錄的插入順序單獨存儲在一個文件中,稱之爲數據文件。這個文件並不劃分爲若干個數據頁,有多少記錄就往這個文件中塞多少記錄就成了。我們可以通過行號而快速訪問到一條記錄。MyISAM記錄也需要記錄頭信息來存儲一些額外數據。
  2. 使用MyISAM存儲引擎的表會把索引信息另外存儲到一個稱爲索引文件的另一個文件中。MyISAM會單獨爲表的主鍵創建一個索引,只不過在索引的葉子節點中存儲的不是完整的用戶記錄,而是主鍵值 + 行號的組合。也就是先通過索引找到對應的行號,再通過行號去找對應的記錄!所以MyISAM中建立的索引相當於全部都是二級索引
  3. 如果有需要的話,我們也可以對其它的列分別建立索引或者建立聯合索引,原理和InnoDB中的索引差不多,不過在葉子節點處存儲的是相應的列 + 行號。這些索引也全部都是二級索引。

問題解答

  1. 查詢時條件字段加索引爲什麼比不加索引快?因爲加了索引以後相當於有了一個目錄,就像字典目錄一樣,當然很快。
  2. 爲什麼MySQL建議使用自增主鍵而非UUID主鍵?因爲B+樹每個節點內的記錄都是按照索引列的值從小到大的順序形成的單鏈表,自增主鍵都是插入鏈表末尾,而UUID主鍵需要比較記錄的主鍵值,容易產生記錄移動以及頁分裂現象,導致性能降低。
  3. 什麼是二級索引?按照其他條件(非主鍵)建立的索引就是二級索引,二級索引的葉子節點包含的用戶記錄由索引列和主鍵組成,所以使用二級索引查詢需要找到主鍵值之後再到聚簇索引中查找完整的用戶記錄,也就是回表。
  4. 爲什麼建立了聯合索引但只匹配索引後邊的列就無法使用索引?B+樹的每個節點之間以及節點內記錄都是先按照聯合索引前邊的列排序,如果該列值相同,再按照聯合索引後邊的列排序。所以如果只匹配索引後邊的列,就無法使用索引。

參考資料

  MySQL 是怎樣運行的:從根兒上理解 MySQL

發佈了33 篇原創文章 · 獲贊 1 · 訪問量 8747
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章