Mysql索引使用、索引優化(二)

一、什麼情況下適合使用索引?

  1. 字段的數值有唯一性的限制,比如用戶名,可以用唯一索引或者主鍵索引
  2. 頻繁作爲 WHERE 查詢條件的字段,尤其在數據表大的情況下
  3. 需要經常 GROUP BY 和 ORDER BY 的列
    1. 索引就是讓數據按照某種順序進行存儲或檢索,因此當我們使用 GROUP BY 對數據進行分組查詢,或者使用 ORDER BY 對數據進行排序的時候,就需要對分組或者排序的字段進行索引。
    2. 如果我們需要對不同字段同時進行 GROUP BY 和 ORDER BY,那麼是不是需要單獨創建不同的索引呢?
      當我們對 user_id 和 comment_time 分別創建索引,執行下面的 SQL 查詢:
SELECT user_id, count(*) as num FROM product_comment group by user_id order by comment_time desc limit 100

運行結果(運行時間 >100s):
實際上多個單列索引在多條件查詢時只會生效一個索引(MySQL 會選擇其中一個限制最嚴格的作爲索引),所以在多條件聯合查詢的時候最好創建聯合索引。
我們創建聯合索引 (user_id, comment_time),再來看下查詢的時間,查詢時間爲 0.775s
如果我們創建聯合索引的順序爲 (comment_time, user_id) 呢?運行時間爲 1.990s,
這是因爲在進行 SELECT 查詢的時候,先進行 GROUP BY,再對數據進行 ORDER BY 的操作,所以按照這個聯合索引的順序效率是最高的。

  1. UPDATE、DELETE 的 WHERE 條件列,一般也需要創建索引
  2. DISTINCT 字段需要創建索引
  3. 做多表 JOIN 連接操作時,創建索引需要注意以下的原則
    1. 首先,連接表的數量儘量不要超過 3 張,因爲每增加一張表就相當於增加了一次嵌套的循環,數量級增長會非常快,嚴重影響查詢的效率。
    2. 其次,對 WHERE 條件創建索引,因爲 WHERE 纔是對數據條件的過濾。如果在數據量非常大的情況下,沒有 WHERE 條件過濾是非常可怕的。
    3. 最後,對用於連接的字段創建索引,並且該字段在多張表中的類型必須一致。比如 user_id 在 product_comment 表和 user 表中都爲 int(11) 類型,而不能一個爲 int 另一個爲 varchar 類型。

二、 什麼情況下不適合使用索引?

  1. 在數據表中的數據行數比較少的情況下,比如不到 1000 行,是不需要創建索引的
  2. 當數據重複度大,比如高於 10% 的時候,也不需要對這個字段使用索引 ,如果是性別這個字段,就不需要對它創建索引。
  3. WHERE 條件(包括 GROUP BY、ORDER BY)裏用不到的字段不需要創建索引,索引的價值是快速定位,如果起不到定位的字段通常是不需要創建索引的。
  4. 頻繁更新的字段不一定要創建索引。因爲更新數據的時候,也需要更新索引,如果索引太多,在更新索引的時候也會造成負擔,從而影響效率。

三、什麼情況下索引失效?

  1. 如果索引進行了表達式計算,則會失效
EXPLAIN SELECT comment_id, user_id, comment_text FROM product_comment 
WHERE comment_id+1 = 900001

-- 解決 :重寫sql
SELECT comment_id, user_id, comment_text FROM product_comment 
WHERE comment_id = 900000

  1. 如果對索引使用函數,也會造成失效
-- 對 comment_text 的前三位爲 abc 的內容進行條件篩選
EXPLAIN SELECT comment_id, user_id, comment_text FROM product_comment 
WHERE SUBSTRING(comment_text, 1,3)='abc'

-- 解決 :重寫sql
SELECT comment_id, user_id, comment_text FROM product_comment 
WHERE comment_text LIKE 'abc%'

  1. 在 WHERE 子句中,如果在 OR 前的條件列進行了索引,而在 OR 後的條件列沒有進行索引,那麼索引會失效。
-- comment_id 是主鍵,而 comment_text 沒有進行索引,因爲 OR 的含義就是兩個只要滿足一個即可,
-- 因此只有一個條件列進行了索引是沒有意義的,只要有條件列沒有進行索引,就會進行全表掃描
EXPLAIN SELECT comment_id, user_id, comment_text FROM product_comment 
WHERE comment_id = 900001 OR comment_text = '462eed7ac6e791292a79'

-- 解決 :對comment_text 創建索引
  1. . 當我們使用 LIKE 進行模糊查詢的時候,後面不能是 %
EXPLAIN SELECT comment_id, user_id, comment_text FROM product_comment
 WHERE comment_text LIKE '%abc'
  1. 索引列與 NULL 或者 NOT NULL 進行判斷的時候也會失效。
-- 這是因爲索引並不存儲空值,所以最好在設計數據表的時候就將字段設置爲 NOT NULL 約束,
-- 比如你可以將 INT 類型的字段,默認值設置爲 0。將字符類型的默認值設置爲空字符串 (’’)。
  1. 我們在使用聯合索引的時候要注意最左原則,
    假設我們有 x、y、z 三個字段,創建聯合索引(x, y, z)之後,我們可以把 x、y、z 分別類比成“百分位”、“十分位”和“個位”。

    查詢“x=9 AND y=8 AND z=7”的過程,就是在一個由小到大排列的數值序列中尋找“987”,可以很快找到。

    查詢“y=8 AND z=7”,就用不上索引了,因爲可能存在 187、287、387、487………這樣就必須掃描所有數值。

    查詢“z=7 AND y=8 AND x=9”的時候,如果三個字段 x、y、z 在條件查詢的時候是亂序的,但採用的是等值查詢(=)或者是 IN 查詢,那麼 MySQL 的優化器可以自動幫我們調整爲可以使用聯合索引的形式。

    當我們查詢“x=9 AND y>8 AND z=7”的時候,如果建立了 (x,y,z) 順序的索引,這時候 z 是用不上索引的。這是因爲 MySQL 在匹配聯合索引最左前綴的時候,如果遇到了範圍查詢,比如(<)(>)和 between 等,就會停止匹配。索引列最多作用於一個範圍列,對於後面的 Z 來說,就沒法使用到索引了。

    通過這個我們也可以知道,聯合索引的最左前綴匹配原則針對的是創建的聯合索引中的順序,如果創建了聯合索引(x,y,z),那麼這個索引的使用順序就很重要了。如果在條件語句中只有 y 和 z,那麼就用不上聯合索引。

    此外,SQL 條件語句中的字段順序並不重要,因爲在邏輯查詢優化階段會自動進行查詢重寫。

    最後你需要記住,如果我們遇到了範圍條件查詢,比如(<)(<=)(>)(>=)和 between 等,那麼範圍列後的列就無法使用到索引了。

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章