一、MySQL索引簡介
索引是 MySQL數據庫中的重要對象之一,用於快速找出某個列中有某一特定值的行。
為什么要使用索引
索引是 MySQL 中一種十分重要的數據庫對象。它是數據庫性能調優技術的基礎,常用於實現數據的快速檢索。
索引就是根據表中的一列或若干列按照一定順序建立的列值與記錄行之間的對應關系表,實質上是一張描述索引列的列值與原表中記錄行之間一一對應關系的有序表。
在 MySQL 中,通常有以下兩種方式訪問數據庫表的行數據:
1) 順序訪問
順序訪問是在表中實行全表掃描,從頭到尾逐行遍歷,直到在無序的行數據中找到符合條件的目標數據。這種方式實現比較簡單,但是當表中有大量數據的時候,效率非常低下。例如,在幾千萬條數據中查找少量的數據時,使用順序訪問方式將會遍歷所有的數據,花費大量的時間,顯然會影響數據庫的處理性能。
2) 索引訪問
索引訪問是通過遍歷索引來直接訪問表中記錄行的方式。使用這種方式的前提是對表建立一個索引,在列上創建了索引之后,查找數據時可以直接根據該列上的索引找到對應記錄行的位置,從而快捷地查找到數據。索引存儲了指定列數據值的指針,根據指定的排序順序對這些指針排序。
例如,在學生基本信息表 students 中,如果基於 student_id 建立了索引,系統就建立了一張索引列到實際記錄的映射表,當用戶需要查找 student_id 為 12022 的數據的時候,系統先在 student_id 索引上找到該記錄,然后通過映射表直接找到數據行,並且返回該行數據。因為掃描索引的速度一般遠遠大於掃描實際數據行的速度,所以采用索引的方式可以大大提高數據庫的工作效率。
索引的分類
索引的類型和存儲引擎有關,每種存儲引擎所支持的索引類型不一定完全相同。根據存儲方式的不同,MySQL 中常用的索引在物理上分為以下兩類。
1) B-樹索引
B-樹索引又稱為 BTREE 索引,目前大部分的索引都是采用 B-樹索引來存儲的。B-樹索引是一個典型的數據結構,其包含的組件主要有以下幾個:
- 葉子節點:包含的條目直接指向表里的數據行。葉子節點之間彼此相連,一個葉子節點有一個指向下一個葉子節點的指針。
- 分支節點:包含的條目指向索引里其他的分支節點或者葉子節點。
- 根節點:一個 B-樹索引只有一個根節點,實際上就是位於樹的最頂端的分支節點。
基於這種樹形數據結構,表中的每一行都會在索引上有一個對應值。因此,在表中進行數據查詢時,可以根據索引值一步一步定位到數據所在的行。
B-樹索引可以進行全鍵值、鍵值范圍和鍵值前綴查詢,也可以對查詢結果進行 ORDER BY 排序。但 B-樹索引必須遵循左邊前綴原則,要考慮以下幾點約束:
- 查詢必須從索引的最左邊的列開始。
- 查詢不能跳過某一索引列,必須按照從左到右的順序進行匹配。
- 存儲引擎不能使用索引中范圍條件右邊的列。
2) 哈希索引
哈希(Hash)一般翻譯為“散列”,也有直接音譯成“哈希”的,就是把任意長度的輸入(又叫作預映射,pre-image)通過散列算法變換成固定長度的輸出,該輸出就是散列值。
哈希索引也稱為散列索引或 HASH 索引。MySQL 目前僅有 MEMORY 存儲引擎和 HEAP 存儲引擎支持這類索引。其中,MEMORY 存儲引擎可以支持 B- 樹索引和 HASH 索引,且將 HASH 當成默認索引。
HASH 索引不是基於樹形的數據結構查找數據,而是根據索引列對應的哈希值的方法獲取表的記錄行。哈希索引的最大特點是訪問速度快,但也存在下面的一些缺點:
- MySQL 需要讀取表中索引列的值來參與散列計算,散列計算是一個比較耗時的操作。也就是說,相對於 B- 樹索引來說,建立哈希索引會耗費更多的時間。
- 不能使用 HASH 索引排序。
- HASH 索引只支持等值比較,如“=”“IN()”或“<=>”。
- HASH 索引不支持鍵的部分匹配,因為在計算 HASH 值的時候是通過整個索引值來計算的。
根據索引的具體用途,MySQL 中的索引在邏輯上分為以下 5 類:
1) 普通索引
普通索引是最基本的索引類型,唯一任務是加快對數據的訪問速度,沒有任何限制。創建普通索引時,通常使用的關鍵字是 INDEX 或 KEY。
2) 唯一性索引
唯一性索引是不允許索引列具有相同索引值的索引。如果能確定某個數據列只包含彼此各不相同的值,在為這個數據列創建索引的時候就應該用關鍵字 UNIQUE 把它定義為一個唯一性索引。
創建唯一性索引的目的往往不是為了提高訪問速度,而是為了避免數據出現重復。
3) 主鍵索引
主鍵索引是一種唯一性索引,即不允許值重復或者值為空,並且每個表只能有一個主鍵。主鍵可以在創建表的時候指定,也可以通過修改表的方式添加,必須指定關鍵字 PRIMARY KEY。
注意:主鍵是數據庫考察的重點。注意每個表只能有一個主鍵。
4) 空間索引
空間索引主要用於地理空間數據類型 GEOMETRY。
5) 全文索引
全文索引只能在 VARCHAR 或 TEXT 類型的列上創建,並且只能在 MyISAM 表中創建。
索引在邏輯上分為以上 5 類,但在實際使用中,索引通常被創建成單列索引和組合索引。
- 單列索引就是索引只包含原表的一個列。
- 組合索引也稱為復合索引或多列索引,相對於單列索引來說,組合索引是將原表的多個列共同組成一個索引。
提示:一個表可以有多個單列索引,但這些索引不是組合索引。一個組合索引實質上為表的查詢提供了多個索引,以此來加快查詢速度。比如,在一個表中創建了一個組合索引(c1,c2,c3),在實際查詢中,系統用來實際加速的索引有三個:單個索引(c1)、雙列索引(c1,c2)和多列索引(c1,c2,c3)。
為了提高索引的應用性能,MySQL中的索引可以根據具體應用采用不同的索引策略。這些索引策略所對應的索引類型有聚集索引、次要索引、覆蓋索引、復合索引、前綴索引、唯一索引等。
索引的使用原則和注意事項
雖然索引可以加快查詢速度,提高 MySQL 的處理性能,但是過多地使用索引也會造成以下弊端:
- 創建索引和維護索引要耗費時間,這種時間隨着數據量的增加而增加。
- 除了數據表占數據空間之外,每一個索引還要占一定的物理空間。如果要建立聚簇索引,那么需要的空間就會更大。
- 當對表中的數據進行增加、刪除和修改的時候,索引也要動態地維護,這樣就降低了數據的維護速度。
注意:索引可以在一些情況下加速查詢,但是在某些情況下,會降低效率。
索引只是提高效率的一個因素,因此在建立索引的時候應該遵循以下原則:
- 在經常需要搜索的列上建立索引,可以加快搜索的速度。
- 在作為主鍵的列上創建索引,強制該列的唯一性,並組織表中數據的排列結構。
- 在經常使用表連接的列上創建索引,這些列主要是一些外鍵,可以加快表連接的速度。
- 在經常需要根據范圍進行搜索的列上創建索引,因為索引已經排序,所以其指定的范圍是連續的。
- 在經常需要排序的列上創建索引,因為索引已經排序,所以查詢時可以利用索引的排序,加快排序查詢。
- 在經常使用 WHERE 子句的列上創建索引,加快條件的判斷速度。
與此對應,在某些應用場合下建立索引不能提高 MySQL 的工作效率,甚至在一定程度上還帶來負面效應,降低了數據庫的工作效率,一般來說不適合創建索引的環境如下:
- 對於那些在查詢中很少使用或參考的列不應該創建索引。因為這些列很少使用到,所以有索引或者無索引並不能提高查詢速度。相反,由於增加了索引,反而降低了系統的維護速度,並增大了空間要求。
- 對於那些只有很少數據值的列也不應該創建索引。因為這些列的取值很少,例如人事表的性別列。查詢結果集的數據行占了表中數據行的很大比例,增加索引並不能明顯加快檢索速度。
- 對於那些定義為 TEXT、IMAGE 和 BIT 數據類型的列不應該創建索引。因為這些列的數據量要么相當大,要么取值很少。
- 當修改性能遠遠大於檢索性能時,不應該創建索引。因為修改性能和檢索性能是互相矛盾的。當創建索引時,會提高檢索性能,降低修改性能。當減少索引時,會提高修改性能,降低檢索性能。因此,當修改性能遠遠大於檢索性能時,不應該創建索引。
二、MySQL創建索引(CREATE INDEX)
索引的建立對於 MySQL數據庫的高效運行是很重要的,索引可以大大提升 MySQL 的檢索速度。
基本語法
MySQL 提供了三種創建索引的方法:
1) 使用 CREATE INDEX 語句
可以使用專門用於創建索引的 CREATE INDEX 語句在一個已有的表上創建索引,但該語句不能創建主鍵。
語法格式:
CREATE <索引名> ON <表名> (<列名> [<長度>] [ ASC | DESC])
語法說明如下:
<索引名>:指定索引名。一個表可以創建多個索引,但每個索引在該表中的名稱是唯一的。
<表名>:指定要創建索引的表名。
<列名>:指定要創建索引的列名。通常可以考慮將查詢語句中在 JOIN 子句和 WHERE 子句里經常出現的列作為索引列。
<長度>:可選項。指定使用列前的 length 個字符來創建索引。使用列的一部分創建索引有利於減小索引文件的大小,節省索引列所占的空間。在某些情況下,只能對列的前綴進行索引。索引列的長度有一個最大上限 255 個字節(MyISAM 和 InnoDB 表的最大上限為 1000 個字節),如果索引列的長度超過了這個上限,就只能用列的前綴進行索引。另外,BLOB 或 TEXT 類型的列也必須使用前綴索引。
ASC|DESC:可選項。ASC指定索引按照升序來排列,DESC指定索引按照降序來排列,默認為ASC。
【實例1】在tb_stu_info表上創建索引,以列id為索引列


2) 使用 CREATE TABLE 語句
索引也可以在創建表(CREATE TABLE)的同時創建。在 CREATE TABLE 語句中添加以下語句。
語法格式:
CONSTRAINT PRIMARY KEY [索引類型] (<列名>,…)
在 CREATE TABLE 語句中添加此語句,表示在創建新表的同時創建該表的主鍵。
【實例2】創建一個表 tb_stu_info2,在該表的 id 字段上使用 PRIMARY KEY 關鍵字創建主鍵索引。


語法格式:
KEY | INDEX [<索引名>] [<索引類型>] (<列名>,…)
在 CREATE TABLE 語句中添加此語句,表示在創建新表的同時創建該表的索引。
【實例 3】創建一個表 tb_stu_info3,在該表的 height 字段創建一般索引。

語法格式:
UNIQUE [ INDEX | KEY] [<索引名>] [<索引類型>] (<列名>,…)
在 CREATE TABLE 語句中添加此語句,表示在創建新表的同時創建該表的唯一性索引。
【實例 4】創建一個表 tb_stu_info4,在該表的 id 字段上使用 UNIQUE 關鍵字創建唯一索引。

語法格式:
FOREIGN KEY <索引名> <列名>
在 CREATE TABLE 語句中添加此語句,表示在創建新表的同時創建該表的外鍵。
【實例 5】創建一個表 tb_stu_info5,在該表的 dept_id字段上使用 FOREIGN KEY關鍵字創建外鍵索引。

在使用 CREATE TABLE 語句定義列選項的時候,可以通過直接在某個列定義后面添加 PRIMARY KEY 的方式創建主鍵。而當主鍵是由多個列組成的多列索引時,則不能使用這種方法,只能用在語句的最后加上一個 PRIMARY KRY(<列名>,…) 子句的方式來實現。
3) 使用 ALTER TABLE 語句
CREATE INDEX 語句可以在一個已有的表上創建索引,ALTER TABLE 語句也可以在一個已有的表上創建索引。在使用 ALTER TABLE 語句修改表的同時,可以向已有的表添加索引。具體的做法是在 ALTER TABLE 語句中添加以下語法成分的某一項或幾項。
語法格式:
ADD INDEX [<索引名>] [<索引類型>] (<列名>,…)
在 ALTER TABLE 語句中添加此語法成分,表示在修改表的同時為該表添加索引。
【實例 5】創建一個表 tb_stu_info6,在該表的id字段添創建一般索引。


語法格式:
ADD PRIMARY KEY [<索引類型>] (<列名>,…)
在 ALTER TABLE 語句中添加此語法成分,表示在修改表的同時為該表添加主鍵。
【實例 7】創建一個表 tb_stu_info7,在該表的 id 字段上使用 PRIMARY 關鍵字創建主鍵索引。


語法格式:
ADD UNIQUE [ INDEX | KEY] [<索引名>] [<索引類型>] (<列名>,…)
在 ALTER TABLE 語句中添加此語法成分,表示在修改表的同時為該表添加唯一性索引。
【實例 8】創建一個表 tb_stu_info8,在該表的 id 字段上使用 UNIQUE 關鍵字創建唯一索引。

語法格式:
ADD FOREIGN KEY [<索引名>] (<列名>,…)
在 ALTER TABLE 語句中添加此語法成分,表示在修改表的同時為該表添加外鍵。
【實例 9】創建一個表 tb_stu_info9,在該表的 dept_id 字段上使用 FOREIGN關鍵字創建外鍵索引。

查看索引
在 MySQL 中,如果要查看已創建的索引的情況,可以使用 SHOW INDEX 語句查看表中創建的索引。
語法格式:
SHOW INDEX FROM <表名> [ FROM <數據庫名>]
語法說明如下:
<表名>:要顯示索引的表。
<數據庫名>:要顯示的表所在的數據庫。
顯示數據庫 mytest 的表 course 的索引情況。
mysql> SHOW INDEX FROM course FROM mytest;
該語句會返回一張結果表,該表有如下幾個字段,每個字段所顯示的內容說明如下。
- Table:表的名稱。
- Non_unique:用於顯示該索引是否是唯一索引。若不是唯一索引,則該列的值顯示為 1;若是唯一索引,則該列的值顯示為 0。
- Key_name:索引的名稱。
- Seq_in_index:索引中的列序列號,從 1 開始計數。
- Column_name:列名稱。
- Collation:顯示列以何種順序存儲在索引中。在 MySQL 中,升序顯示值“A”(升序),若顯示為 NULL,則表示無分類。
- Cardinality:顯示索引中唯一值數目的估計值。基數根據被存儲為整數的統計數據計數,所以即使對於小型表,該值也沒有必要是精確的。基數越大,當進行聯合時,MySQL 使用該索引的機會就越大。
- Sub_part:若列只是被部分編入索引,則為被編入索引的字符的數目。若整列被編入索引,則為 NULL。
- Packed:指示關鍵字如何被壓縮。若沒有被壓縮,則為 NULL。
- Null:用於顯示索引列中是否包含 NULL。若列含有 NULL,則顯示為 YES。若沒有,則該列顯示為 NO。
- Index_type:顯示索引使用的類型和方法(BTREE、FULLTEXT、HASH、RTREE)。
- Comment:顯示評注。
三、MySQL修改和刪除索引(DROP INDEX)
在 MySQL中修改索引可以通過刪除原索引,再根據需要創建一個同名的索引,從而實現修改索引的操作。
基本語法
當不再需要索引時,可以使用 DROP INDEX 語句或 ALTER TABLE 語句來對索引進行刪除。
1) 使用 DROP INDEX 語句
語法格式:
DROP INDEX <索引名> ON <表名>
語法說明如下:
<索引名>:要刪除的索引名。
<表名>:指定該索引所在的表名。
2) 使用 ALTER TABLE 語句
根據 ALTER TABLE 語句的語法可知,該語句也可以用於刪除索引。具體使用方法是將 ALTER TABLE 語句的語法中部分指定為以下子句中的某一項。
- DROP PRIMARY KEY:表示刪除表中的主鍵。一個表只有一個主鍵,主鍵也是一個索引。
- DROP INDEX index_name:表示刪除名稱為 index_name 的索引。
- DROP FOREIGN KEY fk_symbol:表示刪除外鍵。
注意:如果刪除的列是索引的組成部分,那么在刪除該列時,也會將該列從索引中刪除;如果組成索引的所有列都被刪除,那么整個索引將被刪除。
刪除索引
【實例 1】刪除表 tb_stu_info 中的索引,輸入的 SQL 語句和執行結果如下所示。

【實例 2】刪除表 tb_stu_info3 中名稱為 height的索引,輸入的 SQL 語句和執行結果如下所示。
