談談MYSQL索引是如何提升查詢效率的

簡介: 什麼是索引,索引的底層數據結構,索引的幾種類型git

文章已收錄Github精選,歡迎Star:https://github.com/yehongzhi/learningSummary程序員

前言

咱們都知道當查詢數據庫變慢時,須要建索引去優化。可是隻知道索引能優化顯然是不夠的,咱們更應該知道索引的原理,由於不是加了索引就必定會提高性能。那麼接下來就一塊兒探索MYSQL索引的原理吧。github

什麼是索引

索引實際上是一種能高效幫助MYSQL獲取數據的數據結構,一般保存在磁盤文件中,比如一本書的目錄,能加快數據庫的查詢速度。除此以外,索引是有序的,因此也能提升數據的排序效率。數據庫

一般MYSQL的索引包括聚簇索引,覆蓋索引,複合索引,惟一索引,普通索引,一般底層是B+樹的數據結構。數據結構

總結一下,索引的優點在於:性能

  • 提升查詢效率。
  • 下降數據排序的成本。

缺點在於:優化

  • 索引會佔用磁盤空間。
  • 索引會下降更新表的效率。由於在更新數據時,要額外維護索引文件。

索引的類型

  • 聚簇索引

索引列的值必須是惟一的,而且不能爲空,一個表只能有一個聚簇索引。阿里雲

  • 惟一索引

索引列的值是惟一的,值能夠爲空。url

  • 普通索引

沒有什麼限制,容許在定義索引的列中插入重複值和空值。spa

  • 複合索引

也叫組合索引,用戶能夠在多個列上組合創建索引,遵循「最左匹配原則」,在條件容許的狀況下使用複合索引能夠替代多個單列索引的使用。

索引的數據結構

咱們都知道索引的底層數據結構採用的是B+樹,可是在講B+樹以前,要先知道B樹,由於B+樹是在B樹上面進行改進優化的。

首先講一下B樹的特色:

  • B樹的每一個節點都存儲了多個元素,每一個內節點都有多個分支。
  • 節點中元素包含鍵值和數據,節點中的鍵值從小到大排序。
  • 父節點的數據不會出如今子節點中。
  • 全部的葉子節點都在同一層,葉節點具備相同的深度。

在上面的B樹中,假如咱們要找值等於18的數據,查找路徑就是磁盤塊1->磁盤塊3->磁盤塊8。

過程以下:

第一次磁盤IO:首先加載磁盤塊1到內存中,在內存中遍歷比較,由於17<18<50,因此走中間P2,定位到磁盤塊3。

第二次磁盤IO:加載磁盤塊3到內存,依然是遍歷比較,18<25,因此走左邊P1,定位到磁盤塊8。

第三次磁盤IO:加載磁盤塊8到內存,在內存中遍歷,18=18,找到18,取出data。

如圖所示:

若是data存儲的是行數據,直接返回,若是存的是磁盤地址則根據磁盤地址到磁盤中取出數據。能夠看出B樹的查詢效率是很高的。

B樹存在着什麼問題,須要改進優化呢?

第一個問題:B樹在範圍查詢時,性能並不理想。假如要查詢13到30之間的數據,查詢到13後又要回到根節點再去查詢後面的數據,就會產生屢次的查詢遍歷。

第二個問題:由於非葉子節點和葉子節點都會存儲數據,因此佔用的空間大,一個頁可存儲的數據量就會變少,樹的高度就會變高,磁盤的IO次數就會變多。

基於以上兩個問題,就出現了B樹的升級版,B+樹。

B+樹與B樹最大的區別在於兩點:

  • B+樹只有葉子節點存儲數據,非葉子節點只存儲鍵值。而B樹的非葉子節點和葉子節點都會存儲數據。
  • B+樹的最底層的葉子節點會造成一個雙向有序鏈表,而B樹不會。

如圖所示:

B+樹的等值查詢過程是怎麼樣的?

若是在B+樹中進行等值查詢,好比查詢等於13的數據。

查詢路徑爲:磁盤塊1->磁盤塊2->磁盤塊6。

第一次IO:加載磁盤塊1,在內存中遍歷比較,13<17,走左邊,找到磁盤塊2。

第二次IO:加載磁盤塊2,在內存中遍歷比較,10<13<15,走中間,找到磁盤塊6。

第三次IO:加載磁盤塊6,依次遍歷,找到13=13,取出data。

因此B+樹在等值查詢的效率是很高的。

B+樹的範圍查詢過程又是怎麼樣呢?

好比咱們要進行範圍查詢,查詢大於5而且小於15的數據。

查詢路徑爲:磁盤塊1->磁盤塊2->磁盤塊5->磁盤塊6。

第一次IO:加載磁盤塊1,比較得出5<17,而後走左邊,找到磁盤塊2。

第二次IO:加載磁盤塊2,比較5<10,而後仍是走左邊,找到磁盤塊5。

第三次IO:加載磁盤塊5,而後找大於5的數據。

第四次IO:因爲最底層是有序的雙向鏈表,因此繼續往右遍歷便可,直到不符合小於15的數據爲止。

過程如圖所示:

因此在範圍查詢的時候,是不須要像B樹同樣,再回到根節點,這就是底層採用雙向鏈表的好處。

因此B+樹的優點在於,能保證等值查詢和範圍查詢的快速查找

InnoDB索引

咱們經常使用的MySQL存儲引擎通常是InnoDB,因此接下來說講幾種不一樣的索引的底層數據結構,以及查找過程。

聚簇索引

前面講過,每一個InnoDB表有且僅有一個聚簇索引。除此以外,聚簇索引在表的建立有如下幾點規則:

  • 在表中,若是定義了主鍵,InnoDB會將主鍵索引做爲聚簇索引。
  • 若是沒有定義主鍵,則會選擇第一個不爲NULL的惟一索引列做爲聚簇索引。
  • 若是以上兩個都沒有。InnoDB 會使用一個6 字節長整型的隱式字段 ROWID字段構建聚簇索引。該ROWID字段會在插入新行時自動遞增。

除了聚簇索引以外的索引都稱爲非聚簇索引,區別在於,聚簇索引的葉子節點存儲的數據是整行數據,而非聚簇索引存儲的是該行的主鍵值。

好比有一張user表,如圖所示:

底層的數據結構就像這樣:

當咱們用主鍵值去查詢的時候,查詢效率是很快的,由於能夠直接返回數據。

普通索引

也就是用得最多的一種索引,好比我要爲user表的age列建立索引,SQL語句能夠這樣寫:

CREATE INDEX INDEX_USER_AGE ON `user`(age);

普通索引屬於非聚簇索引,因此葉子節點存儲的是主鍵值,底層的數據結構大概長這個樣子:

好比要查詢age=33的數據,那麼首先查到磁盤塊7的age=33的數據,獲取到主鍵值,主鍵值爲4。

接着再經過主鍵值等於4,查詢到該行的數據。因此總得來講,底層會進行兩次查詢。

這種先經過查詢主鍵值,再經過主鍵值查詢到數據的過程就叫作回表查詢。

覆蓋索引

既然上面提到了回表查詢,那麼天然而然會想到,有沒有什麼辦法能避免回表查詢呢?答案確定是有的,那就是使用覆蓋索引。

覆蓋索引不是一種索引的類型,而是一種使用索引的方式。假設你須要查詢的列是創建了索引,查詢的結果在索引列上就能獲取,那就能夠用覆蓋索引。

好比上面的例子,咱們經過age=33查詢,我須要查詢的結果就只要age這一列,那就能夠用到覆蓋索引,如圖所示:

使用到覆蓋索引的話,就能避免回表查詢,因此在寫SQL語句時儘可能不要寫SELECT *。

總結

這篇文章主要講的是索引的類型,索引的數據結構,以及InnoDB表中經常使用的幾種索引。固然,除了上述講的這些以外,還有不少關於索引的知識,好比索引失效的場景,索引建立的原則等等,因爲篇幅過長,留着之後再講。

那麼這篇文章就寫到這裏了,感謝你們的閱讀。

以爲有用就點個贊吧,你的點贊是我創做的最大動力~

我是一個努力讓你們記住的程序員。咱們下期再見!!!

https://developer.aliyun.com/article/784382?utm_content=g_1000275186

本文爲阿里雲原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索