1. 程式人生 > 實用技巧 >1、MySQL索引分析

1、MySQL索引分析

索引優化分析

一、索引基礎

1、索引
  • 索引(Index)是幫助 MySQL 高效獲取資料的資料結構。

  • 索引是資料結構。可以簡單理解為排好序的快速查詢資料結構。

  • 一般來說索引本身也很大,不可能全部儲存在記憶體中,因此索引往往以索引檔案的形式儲存在磁碟上。

2、索引優缺點

優勢:

  • 提高資料檢索的效率,降低資料庫的IO成本。
  • 通過索引列對資料進行排序,降低資料排序的成本,降低了CPU的消耗。

劣勢:

  • 索引大大提高了查詢速度,同時卻會降低更新表的速度

  • 實際上索引也是一張表,該表儲存了主鍵與索引欄位,並指向實體表的記錄,所以索引列也是要佔用空間

    的。

二、MySQL索引

MySQL使用的索引的資料結構是BTree。

  • B-Tree是為磁碟等外儲存裝置設計的一種平衡查詢樹。

  • 系統從磁碟讀取資料到記憶體時是以磁碟塊(block)為基本單位的,位於同一個磁碟塊中的資料會被一次性讀取出來,而不是需要什麼取什麼。

  • InnoDB儲存引擎中有頁(Page)的概念,頁是其磁碟管理的最小單位。InnoDB儲存引擎中預設每個頁的大小為16KB,可通過引數innodb_page_size將頁的大小設定為4K、8K、16K,在MySQL中可通過如下命令檢視頁的大小:

    mysql> show variables like 'innodb_page_size';

  • 而系統一個磁碟塊的儲存空間往往沒有這麼大,因此InnoDB每次申請磁碟空間時都會是若干地址連續磁碟塊來達到頁的大小16KB。InnoDB在把磁碟資料讀入到磁碟時會以頁為基本單位,在查詢資料時如果一個頁中的每條資料都能有助於定位資料記錄的位置,這將會減少磁碟I/O次數,提高查詢效率。

  • B-Tree結構的資料可以讓系統高效的找到資料所在的磁碟塊。為了描述B-Tree,首先定義一條記錄為一個二元組[key, data] ,key為記錄的鍵值,對應表中的主鍵值,data為一行記錄中除主鍵外的資料。對於不同的記錄,key值互不相同

1、BTree(平衡多路查詢樹):

1.1 一顆 m 階B樹:

  • 1、 樹中每個節點最多有 m 棵子樹(即含有最多 m-1 個關鍵字)

  • 2.、若根節點不是終端節點,則最少含有2棵子樹

  • 3、除根節點外的所有非葉節點最少有Ceil( m/2 )棵子樹(即最少含有Ceil( m/2 ) - 1 個關鍵字)注:ceil()是個朝正無窮方向取整的函式 如ceil(1.1)結果為2);

  • 4、所有非葉子節點的結構為:

n P0 K1 P1 K2 P2 ... Kn Pn
指向子樹的指標 關鍵字
  • 5、所有葉節點都出現在同一個層次上,且葉節點不帶任何值,代表查詢失敗

1.2 實際應用中 3 階 B樹:

  • 每個節點佔用一個盤塊的磁碟空間,

  • 一個節點上有兩個升序排序的關鍵字和三個指向子樹根節點的指標

  • 指標儲存的是子節點所在磁碟塊的地址。兩個關鍵詞劃分成的三個範圍域對應三個指標指向的子樹的資料的範圍域。

1.3 BTree 的效果

​ 磁碟塊讀入記憶體後,由於記憶體中的關鍵字是一個有序表結構,可以利用二分法查詢提高效率。而磁碟I/O操作是影響整個B-Tree查詢效率的決定因素。B-Tree相對於AVLTree縮減了節點個數,使每次磁碟I/O取到記憶體的資料都發揮了作用,從而提高了查詢效率。

2、B+Tree
  • B+Tree是在B-Tree基礎上的一種優化,使其更適合實現外儲存索引結構,InnoDB儲存引擎就是用B+Tree實現其索引結構。

  • 從上一節中的B-Tree結構圖中可以看到每個節點中不僅包含資料的key值,還有data值。而每一個頁的儲存空間是有限的,如果data資料較大時將會導致每個節點(即一個頁)能儲存的key的數量很小,當儲存的資料量很大時同樣會導致B-Tree的深度較大,增大查詢時的磁碟I/O次數,進而影響查詢效率。在B+Tree中,所有資料記錄節點都是按照鍵值大小順序存放在同一層的葉子節點上,而非葉子節點上只儲存key值資訊,這樣可以大大加大每個節點儲存的key值數量,降低B+Tree的高度。

2.1 B+Tree相對於B-Tree有幾點不同:

  • 非葉子節點只儲存鍵值資訊。
  • 所有葉子節點之間都有一個鏈指標。
  • 資料記錄都存放在葉子節點中。其他節點作為索引

1.2 B+Tree的優點

    1. InnoDB儲存引擎中頁的大小為16KB
    1. 一般表的主鍵型別為INT(佔用4個位元組)或BIGINT(佔用8個位元組),一般表的主鍵型別為INT(佔用4個位元組)或BIGINT(佔用8個位元組),指標型別也一般為4或8個位元組,
    1. 因此一個頁(B+Tree中的一個節點)中大概儲存16KB/(8B+8B)=1K個鍵值
    1. 也就是說一個深度為3的B+Tree索引可以維護10^3 * 10^3 * 10^3 = 10億 條記錄。

​ 實際情況中每個節點可能不能填充滿,因此在資料庫中,B+Tree的高度一般都在24層。[mysql](http://lib.csdn.net/base/mysql)的InnoDB儲存引擎在設計時是將根節點常駐記憶體的,也就是說查詢某一鍵值的行記錄時最多隻需要13次磁碟I/O操作

三、 explain (id、type、key、rows、extra 最重要)

1. id
  • id 相同,執行順序由上至下

  • id 不同,id 越大越先被執行。

    • 如果是子查詢,id 的序號會遞增,id 值越大優先順序越高,越先被執行
  • Id 有相同也有不同。

    • id 如果相同,可以認為是一組,從上往下順序執行;
    • 在所有組中,id 值越大,優先順序越高,越先執行衍生 = DERIVED
2. select_type

select_type: 代表查詢的型別,主要是用於區別普通查詢、聯合查詢、子查詢等的複雜查詢。

select_type 含義
1. SIMPLE 1. SIMPLE 代表單表查詢。
2、簡單的 select 查詢,查詢中不包含子查詢或者 UNION
![image-20201025211851005](/Users/renren/Library/Application Support/typora-user-images/image-20201025211851005.png)
2. PRIMARY 1. 查詢中若包含任何複雜的子部分,最外層查詢則被標記為 Primary
![image-20201025212702926](/Users/renren/Library/Application Support/typora-user-images/image-20201025212702926.png)
3. DERIVED 1. 在 FROM 列表中包含的子查詢被標記為 DERIVED(衍生)。
2. MySQL 會遞迴執行這些子查詢, 把結果放在臨時表裡。
4. SUBQUERY 在SELECT或WHERE列表中包含了子查詢
5. DEPEDENT SUBQUERY 在SELECT或WHERE列表中包含了子查詢,子查詢基於外層
6. UNCACHEABLE SUBQUERY 無法使用快取的子查詢
7. UNION 1. 若第二個SELECT出現在UNION之後,則被標記為UNION;
2. 若UNION包含在FROM子句的子查詢中,外層SELECT將被標記為:DERIVED
![image-20201025220553420](/Users/renren/Library/Application Support/typora-user-images/image-20201025220553420.png)
8. UNION RESULT 從UNION表獲取結果的SELECT
3.type

type 是查詢的訪問型別。是較重要的一個指標。結果從最好到最壞依次為:

system > const > eq_ref > ref > range > index > ALL

type 型別 含義
1、system 表只有一行記錄(等於系統表)
這是 const 型別的特例,平時不會出現
2、const 1. 表示通過索引一次就找到了,const 用於比較 primary key 或者 unique 索引。
2. 因為只匹配一行資料,所以很快
3. 如將主鍵置於 where 列表中用=查詢,MySQL 就能將該查詢轉換為一個常量。
![image-20201025221923618](/Users/renren/Library/Application Support/typora-user-images/image-20201025221923618.png)
3、eq_ref 1. 唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或唯一索引掃描。
![image-20201025222937706](/Users/renren/Library/Application Support/typora-user-images/image-20201025222937706.png)
4、ref 非唯一性索引掃描,返回匹配某個單獨值的所有行
本質上也是一種索引訪問,它返回所有匹配某個單獨值的行, 然而,它可能會找到多個符合條件的行
![image-20201025230942869](/Users/renren/Library/Application Support/typora-user-images/image-20201025230942869.png)
5、range 只檢索給定範圍的行,使用一個索引來選擇行。
在你的 where 語句中出現 了 between、<、>、in 等的查詢這種範圍掃描索引
6、index 1. 是sql使用了索引但是沒有通過索引進行過濾
2. 一般是使用了覆蓋索引或者是利用索引進行了排序分組
3. index 只遍歷索引樹,all 遍歷所有資料檔案,索引檔案小的多![image-20201025231639178](/Users/renren/Library/Application Support/typora-user-images/image-20201025231639178.png)
7、all > Full Table Scan,將遍歷全表以找到匹配的行。
> 未使用索引
4. possible_keys
  • 顯示可能應用在這張表中的索引,一個或多個。查詢涉及到的欄位上若存在索引,則該索引將被列出,但不一

定被查詢實際使用。

5. key
  • 實際使用的索引。如果為NULL,則沒有使用索引。
6. key_len
  • 表示索引中使用的位元組數,可通過該列計算查詢中使用的索引的長度。
  • key_len 欄位能夠幫你檢查是否充分的利用上了索引。ken_len 越長,說明索引使用的越充分。
    ![image-20201025232945792](/Users/renren/Library/Application Support/typora-user-images/image-20201025232945792.png)
7. ref
  • 顯示索引的哪一列被使用了
  • 哪些列或常量被用於查詢索引列上的值。
8. rows
  • rows 列顯示 MySQL 認為它執行查詢時必須檢查的行數。越少越好!
9. Extra
  • 額外補充的重要資訊

  • extra 含義
    1、using filesort (檔案內排序)
    1. 說明 mysql 會對資料使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。
    2. MySQL 中無法利用索引完成的順序操作稱為“檔案排序”。
    3. 需要優化
    2、using temporary 1、使用臨時表儲存中間結果;
    2、MySQL對查詢結果進行排序的時候使用了臨時表,常見於 order by 和分組查詢 group by
    3、需要優化
    3、 using index 1、表示select操作中使用了覆蓋索引(covering index),避免訪問表的資料行,效率高
    2、如果同時出現了using where,表示索引被用來執行索引鍵值的查詢
    3、如果沒有同時出現 using where,表示索引用來讀取資料而非執行查詢動作

    覆蓋索引(covering index): select 的資料列只要從索引中就能取得,不必讀取資料行。MySQL可以利用索引返回select 列表中的欄位,而不必根據索引再次讀取資料檔案,即查詢列要被所建的索引覆蓋。select必須要和索引的個數、順序相一致。

    • 如果要使用覆蓋索引,一定要注意select 列表中取所需要的列,而不要 select *,

    • 如果將所有欄位做索引會導致索引檔案過大,查詢效能下降

四、索引優化例項

1、建立表

![image-20201026002827369](/Users/renren/Library/Application Support/typora-user-images/image-20201026002827369.png)

2、分析查詢結果

![image-20201026002913489](/Users/renren/Library/Application Support/typora-user-images/image-20201026002913489.png)

3、建立索引

![image-20201026003308721](/Users/renren/Library/Application Support/typora-user-images/image-20201026003308721.png)

4、查詢結果

![image-20201026003334547](/Users/renren/Library/Application Support/typora-user-images/image-20201026003334547.png)

![image-20201026005721294](/Users/renren/Library/Application Support/typora-user-images/image-20201026005721294.png)

5、重新建立索引

![image-20201026005405674](/Users/renren/Library/Application Support/typora-user-images/image-20201026005405674.png)

6、重新檢視結果

![image-20201026005442592](/Users/renren/Library/Application Support/typora-user-images/image-20201026005442592.png)

7、結論

​ 按照 BTree 的工作原理,對於索引 idx_ccv,會先對 category_id 進行排序,再對comment進行排序,最後對views進行排序,當comment > 1 條件是一個範圍值(range),MySQL 無法利用索引再對後面的views部分進行檢索,即 range 型別查詢欄位後面的索引無效。

![image-20201026010642193](/Users/renren/Library/Application Support/typora-user-images/image-20201026010642193.png)