史上最簡單MySQL教程詳解(進階篇)之索引及失效場合總結

史上最簡單MySQL教程詳解(進階篇)之索引及其失效場合總結

什麼是索引及其作用

在大型數據庫中,一張表通常要容納幾十萬甚至是上百萬的數據,這些表一旦涉及到表連接等複雜操作後,當用戶檢索這麼大的數據量時,如果要將數據庫中所有的數據都與想要查詢的數據進行比較的話,是十分慢的。所以爲了有效的提高查詢速率,避免對於數據庫進行全盤掃面,我們就需要使用到:索引。

MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構,它的存在形式是文件。通俗的說:索引更像是我們在圖書館借書的過程中使用的圖書目錄表。它幫我們將圖書根據書名或者是圖書的類別進行了分類,讓我們可以更加迅速的找到我們所需要的圖書,避免了大範圍的尋找。

索引的種類

索引是在MYSQL的存儲引擎層中實現的,而不是在服務層實現的。所以每種存儲引擎的索引都不一定完全相同,也不是所有的存儲引擎都支持所有的索引類型。MYSQL目前提供了一下4種索引。

  • B-Tree 索引:最常見的索引類型,大部分引擎都支持B樹索引。
  • HASH 索引:只有Memory引擎支持,使用場景簡單。
  • R-Tree 索引(空間索引):空間索引是MyISAM的一種特殊索引類型,主要用於地理空間數據類型。
  • Full-text (全文索引):全文索引也是MyISAM的一種特殊索引類型,主要用於全文索引,InnoDB從MYSQL5.6版本提供對全文索引的支持。

各存儲引擎對於索引的支持

索引 MyISAM引擎 InnoDB引擎 Memory引擎
B-Tree 索引 支持 支持 支持
HASH 索引 不支持 不支持 支持
R-Tree 索引 支持 不支持 不支持
Full-text 索引 不支持 暫不支持 不支持

簡單介紹索引的實現

大多數數據庫都使用了B樹(Balance Tree,平衡樹)的結構來保存索引。B樹就是如下圖所示的一樣,枝葉擴散開來的樹狀結構。

這裏寫圖片描述

B-TREE 每個節點都是一個二元數組: 一個是保存着關鍵字key,另一個則是指針 pointer。指針則是指向數據所存儲的位置。節點就由這些數據相互關聯起來而組成。

最上層的節點被稱爲根節點,最下面的節點被稱爲葉子節點。兩者之間的節點被稱爲中間節點。B樹中最顯著的一個特點就是:根節點到各個葉子節點的距離都相等。也就意味着,檢索任何值都經過相同數目的節點,能提高檢索效率。

查找的原理:B-樹的搜索,從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,如果命中則結束,否則進入查詢關鍵字所屬範圍的子結點;重複,直到所對應的兒子指針爲空,或已經是葉子結點;因此,B-Tree的查找過程是一個順指針查找結點和在結點的關鍵字中進行查找的交叉進行的過程。例如:根據上圖,我們需要查找[K],首先我們去根節點檢索,發現它位於左節點[EH],又去[EH]中查找發現它位於中間節點,就查找到了[K]這個數據。避免了再去檢索右邊部分的節點,有效的提高了檢索的效率。

索引的設置與分析

我們這裏主要講解的是使用最多的B-tree索引。其他類型的索引,小夥伴們可以自行學習。

普通索引

這是最基本的索引類型,而且它沒有唯一性之類的限制。普通索引可以通過以下幾種方式創建: (1)創建索引:使用【CREATE INDEX】命令,語法如下:

CREATE INDEX 索引名 ON 表名(列名);

(2)修改表:使用【ALTER TABLE】命令,語法如下:

ALTER TABLE 表名ADD INDEX 索引名 (列名);

(3)創建表時指定索引:使用【CREATE TABLE 】命令,語法如下:

CREATE TABLE 表名 ( [...], INDEX 索引名 (列名) );

唯一索引(Unique Index )

表示唯一的,不允許重複的索引,如果該字段信息保證不會重複例如身份證號用作索引時,可設置爲unique。UNIQUE索引創建語法和普通索引大體上無異,只是增加了關鍵詞:UNIQUE。
(1)創建索引:

CREATE UNIQUE INDEX 索引名 ON 表名(列名);

(2)修改表:

ALTER TABLE 表名ADD UNIQUE 索引名 (列名);

(3)創建表時指定索引:

CREATE TABLE 表名( [...], UNIQUE 索引名 (列名) );

叢生索引(Clustered Index)

叢生索引是伴隨着主鍵的定義而產生的一種特別唯一性索引,相當於聚合索引,是查找最快的索引。因爲每張表只能有一個主鍵,所以每張表只能有一個叢生索引。但是需要注意注意的是,叢生索引無法通過【CREATE INDEX】語句創建,而是通過定義主鍵來實現:
(1)主鍵一般在創建表的時候指定,語法爲:

CREATE TABLE 表名( [...], PRIMARY KEY (列的列名) );

(2)修改表的方式加入主鍵:

ALTER TABLE 表名ADD PRIMARY KEY (列的列名);

叢生索引與其他索引的區別:

  • 通常索引在葉子節點中保存指向實際數據的指針,而叢生索引的葉子節點保存的是實際數據。
  • 不需要爲保存索引而使用專門的硬盤空間,節約了資源
  • 不需要檢索索引後再訪問數據表,提高了檢索的效率。
  • 創建叢生索引時需要對錶中的數據進行排序,在進行數據插入、更新、刪除時比一般的索引需要耗費更多的時間。

查看索引信息

我們使用【SHOW INDEX】命令來查看錶中所有創建完成的索引信息,語法如下:

SHOW INDEX FROM 表名;

例如:我們對之前已經創建好的【student】表中的【name】字段創建索引,並用【SHOW INDEX】語句查看相關信息:

mysql> CREATE INDEX index_name ON student(name);
Query OK, 5 rows affected (0.10 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> SHOW INDEX FROM student \G
*************************** 1. row ***************************
        Table: student
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: studentId
    Collation: A
  Cardinality: 5
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 2. row ***************************
        Table: student
   Non_unique: 1
     Key_name: collegeId
 Seq_in_index: 1
  Column_name: collegeId
    Collation: A
  Cardinality: NULL
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 3. row ***************************
        Table: student
   Non_unique: 1
     Key_name: index_name
 Seq_in_index: 1
  Column_name: name
    Collation: A
  Cardinality: NULL
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
3 rows in set (0.00 sec)

【SHOW INDEX】執行結果說明:

參數名 說明
Table 表名
Non_unique 是否允許重複(1:允許,0:禁止)
Key_name 索引名
Seq_in_index 索引內的域序號(從1開始)
Column_name 域名
Collation 排序(A:升序,Null:不排序)
Cardinality 索引內的非重複值的數目
Sub_part 作爲索引部分的域內的字符數
Packed 關鍵字的壓縮方式
Null 是否允許爲Null
Index_type 索引的類型
Comment 備註
Index_comment 索引備註

注意事項:

  • 因爲目前MySQL不支持函數索引,但是能對列的前面某一部分進行索引,例如標題title字段,可以只取title的前10個字符進行索引,這個特性可以大大縮小索引文件的大小,但前綴索引也有缺點,在排序Order By和分組Group By 操作的時候無法使用。用戶在設計表結構的時候也可以對文本列根據此特性進行靈活設計,參數【Sub_part】正是針對這種情況產生。語法如下:

    CREATE INDEX 索引名 ON 表名(字段名(所取字符長度))
  • 加入我們在創建索引的時候,同時選擇了多個域,語法如下:

    CREATE INDEX 索引名 ON 表名(列名1,列名2)

    這時,我們創建的索引稱爲複合索引。這是我們再通過【SHOW INDEX】命令來查看索引信息,就會發現【Key_name】一樣,但是【Seq_in_index】顯示的就是列的順序了。

刪除索引

如果需要刪除索引,使用的是【DROP INDEX】命令,語法如下:

DROP INDEX 索引名 ON 表名;

分析索引優劣

當我們學會如何創建、修改和刪除索引以後,我們就可以嘗試着自己去分析索引的使用情況以及索引的好壞。我們使用【EXPLAIN】來確認索引的使用情況。(EXPLAIN是大多數數據庫支持的命令,但是使用方法與返回的信息隨着數據庫的不同可能會有所不同),其的具體語法如下:

EXPLAIN 需要分析的SELECT語句

例如:我們刪除之前在【student】表上創建的索引後,執行Select語句,結果如下:

mysql> EXPLAIN SELECT * FROM student WHERE phone= "135" \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: student
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 5
     filtered: 20.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

【EXPLAIN】命令執行返回參數說明:

參數名 說明
id Select命令的順序號(通常爲1,子查詢時從2開始)

參數【select_type】的返回值說明

說明
SIMPLE 單純的SELECT命令
PRIMARY 最外層的SELECT命令
UNION 由UNION語句連接的Select命令
DEPENDENT UNION 由UNION語句連接的Select命令(依賴外部查詢)
SUBQUERY 子查詢中的SELECT語句
DEPENDENT SUBQUERY 子查詢中的SELECT語句(依賴外部查詢)
DERIVED 派生表(FROM 語句的子查詢)

參數【type】的返回值說明

說明
system 只存在一條數據的表(系統表)
const 擁有PRIMARY KEY/UNION 制約的索引(唯一)
eq_ref 連接時由PRIMARY KEY/UNION 列進行的等值檢索
ref 非UNION列進行的等值檢索
ref_or_null ref中加入了【~OR 列名 IS NULL】的檢索
range 使用索引檢索一定範圍的數據)(=、>、<、IS Null等運算符)
index 全索引掃描
ALL 全表掃描

參數【EXTRA】的返回值說明:

參數 說明
Using where 列數據是從僅僅使用了索引中的信息而沒有讀取實際的行動的表返回的,這發生在對錶的全部的請求列都是同一個索引的部分的時候。
Using temporary 表示MySQL需要使用臨時表來存儲結果集,常見於排序和分組查詢
Using join buffer 說明獲取連接條件時沒有使用索引,並且需要連接緩衝區來存儲中間結果,可能需要添加索引來改進能。
Impossible where 說明where語句會導致沒有符合條件的數據。
Select tables optimized 說明僅通過使用索引,優化器可能僅從聚合函數結果中返回一行
Using filesort 無法利用索引完成排序

我們在經歷過上面的介紹以後,再去查看我們之前執行的結果,我們可以看到遍歷的次數和實際表中的數據(數據量也是5)是相等的,顯然檢索的效率不是很高。如果在大數據量下,這樣的遍歷次數對於查詢速度的影響幾乎是災難的,所以,我們再重新加入所以後再試一次:

mysql> CREATE INDEX index_phone ON student(phone);
Query OK, 5 rows affected (0.10 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> EXPLAIN SELECT * FROM student WHERE phone= "135" \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: student
   partitions: NULL
         type: ref
possible_keys: index_phone
          key: index_phone
      key_len: 767
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

我們發現,遍歷的次數已經降到了只有一次,而且filtered也達到了100%,效率得到了很大程度上的提升。同樣的,如果我們使用索引後,查詢速度未得到改善,我們則需要反思我們的所以設置是否合理,這也就是最簡單的分析索引優劣的方法了。

索引失效場合總結

進行後方一致/部分一致檢索的場合

當我們在使用【LIKE】關鍵詞進行模糊檢索時,只有在前方一致的檢索時能使用上索引,而後方一致或者部分一致是不能使用的。例如下面的兩句查詢語句都是無效的:

SELECT * FROM student WHERE name LIKE "%張%"
SELECT * FROM student WHERE name LIKE "%張"

使用了IS NOT NULL、<>比較運算符的場合

當我們使用了IS NOT NULL、<>比較運算符時,也是不能使用索引的。例如下面的兩句查詢語句都是無效的:

SELECT * FROM student WHERE name IS NOT NULL 
SELECT * FROM student WHERE name <> "張三"

對列使用了運算/函數的場合

當我們在查詢語句中使用了運算或者函數時,索引也是無效的。對於MySQL所具有的函數和運算符可參考這篇博文:史上最簡單MySQL教程詳解(基礎篇)之運算符和常用數據庫函數

複合索引的第一列沒有包含在WHERE條件語句中的場合

例如我們在【student】表創建了基於【name】和【phone】的複合索引,且【name】作爲第一列,那麼下面的兩句查詢語句都是無效的:

Select * FROM student WHERE phone = "135"Select * FROM student WHERE name = "張三" OR phone = "139"

參考文獻

轉載說明: 支持轉載,但請保留原作者,原文鏈接,微信公衆號二維碼

這裏寫圖片描述
掃碼關注作者個人技術公衆號,不定期將有學習資源分享

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