首頁 > 數據庫 > MySQL > 正文

MySQL索引的創建與使用

2021-12-25 16:31:31
字體:
來源:轉載
供稿:網友

索引有很多,且按不同的分類方式,又有很多種分類。不同的數據庫,對索引的支持情況也不盡相同。

聲明:本人主要簡單示例mysql中的單列索引、組合索引的創建與使用。


索引的創建

建表時創建

CREATE TABLE 表名(

字段名 數據類型 [完整性約束條件],

???????……,

[UNIQUE | FULLTEXT | SPATIAL] INDEX | KEY

[索引名](字段名1 [(長度)] [ASC | DESC]) [USING 索引方法]

);

說明:

  • UNIQUE:可選。表示索引為唯一性索引。
  • FULLTEXT:可選。表示索引為全文索引。
  • SPATIAL:可選。表示索引為空間索引。
  • INDEX和KEY:用于指定字段為索引,兩者選擇其中之一就可以了,作用是 ???一樣的。
  • 索引名:可選。給創建的索引取一個新名稱。
  • 字段名1:指定索引對應的字段的名稱,該字段必須是前面定義好的字段。
  • 長度:可選。指索引的長度,必須是字符串類型才可以使用。
  • ASC:可選。表示升序排列。
  • DESC:可選。表示降序排列。

注:索引方法默認使用B+TREE。

單列索引(示例):

CREATE TABLE projectfile (	id INT AUTO_INCREMENT COMMENT '附件id',	fileuploadercode VARCHAR(128) COMMENT '附件上傳者code',	projectid INT COMMENT '項目id;此列受project表中的id列約束',	filename VARCHAR (512) COMMENT '附件名',	fileurl VARCHAR (512) COMMENT '附件下載地址',	filesize BIGINT COMMENT '附件大小,單位Byte',	-- 主鍵本身也是一種索引(注:也可以在上面的創建字段時使該字段主鍵自增)        PRIMARY KEY (id),	-- 主外鍵約束(注:project表中的id字段約束了此表中的projectid字段)	FOREIGN KEY (projectid) REFERENCES project (id),	-- 給projectid字段創建了唯一索引(注:也可以在上面的創建字段時使用unique來創建唯一索引)	UNIQUE INDEX (projectid),	-- 給fileuploadercode字段創建普通索引	INDEX (fileuploadercode)	-- 指定使用INNODB存儲引擎(該引擎支持事務)、utf8字符編碼) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT '項目附件表';

注:這里只為示例如何創建索引,其他的合理性之類的先放一邊。

組合索引(示例):

CREATE TABLE projectfile (	id INT AUTO_INCREMENT COMMENT '附件id',	fileuploadercode VARCHAR(128) COMMENT '附件上傳者code',	projectid INT COMMENT '項目id;此列受project表中的id列約束',	filename VARCHAR (512) COMMENT '附件名',	fileurl VARCHAR (512) COMMENT '附件下載地址',	filesize BIGINT COMMENT '附件大小,單位Byte',	-- 主鍵本身也是一種索引(注:也可以在上面的創建字段時使該字段主鍵自增)        PRIMARY KEY (id),        -- 創建組合索引	INDEX (fileuploadercode,projectid)	-- 指定使用INNODB存儲引擎(該引擎支持事務)、utf8字符編碼) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT '項目附件表';

建表后創建

ALTER?TABLE?表名?ADD?[UNIQUE | FULLTEXT | SPATIAL]??INDEX | KEY??[索引名]?(字段名1 [(長度)] [ASC | DESC])?[USING 索引方法];

CREATE??[UNIQUE | FULLTEXT | SPATIAL]??INDEX??索引名?ON??表名(字段名)?[USING 索引方法];

示例一:

-- 假設建表時fileuploadercode字段沒創建索引(注:同一個字段可以創建多個索引,但一般情況下意義不大)-- 給projectfile表中的fileuploadercode創建索引ALTER TABLE projectfile ADD UNIQUE INDEX (fileuploadercode);

示例二:

ALTER TABLE projectfile ADD INDEX (fileuploadercode, projectid);

示例三:

-- 將id列設置為主鍵ALTER TABLE index_demo ADD PRIMARY KEY(id) ;-- 將id列設置為自增ALTER TABLE index_demo MODIFY id INT auto_increment;  

查看已創建的索引

show index from 表名;

提示:我們也可以直接使用工具查看

示例:


索引的刪除

DROP INDEX 索引名 ON 表名

ALTER TABLE 表名 DROP INDEX?索引名

示例一:

drop index fileuploadercode1 on projectfile;

示例二:

alter table projectfile drop index s2123;

查看SQL語句對索引的使用情況(即:查詢SQL的查詢執行計劃QEP)

在select語句前加上EXPLAIN即可。

示例:

EXPLAIN SELECT * FROM `index_demo` ii WHERE ii.e_name = 'Jane';

分析該SQL的性能為:

提示:我們也可以使用SQL工具查看,如:navicat中的“解釋”選項即可查看。

說明:

id:SELECT識別符。這是SELECT的查詢序列號。

select_type:SELECT類型。

  1. SIMPLE: 簡單SELECT(不使用UNION或子查詢)
  2. PRIMARY: 最外面的SELECT
  3. UNION:UNION中的第二個或后面的SELECT語句
  4. DEPENDENT UNION:UNION中的第二個或后面的SELECT語句,取決于外面的查詢
  5. UNION RESULT:UNION的結果
  6. SUBQUERY:子查詢中的第一個SELECT
  7. DEPENDENT SUBQUERY:子查詢中的第一個SELECT,取決于外面的查詢
  8. DERIVED:導出表的SELECT(FROM子句的子查詢)

table:表名

type:聯接類型。是SQL性能的非常重要的一個指標,結果值從好到壞依次是:system > const > eq_ref > ref
? ? ? ? ? ??> fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL。
? ? ? ? ? ??一般來說,得保證查詢至少達到range級別。

  1. system:表僅有一行(=系統表)。這是const聯接類型的一個特例。
  2. const:表最多有一個匹配行,它將在查詢開始時被讀取。因為僅有一行,在這行的列值可被優化器剩余部分認為是常數。const用于用常數值比較PRIMARY KEY或UNIQUE索引的所有部分時。
  3. eq_ref:對于每個來自于前面的表的行組合,從該表中讀取一行。這可能是最好的聯接類型,除了const類型。它用在一個索引的所有部分被聯接使用并且索引是UNIQUE或PRIMARY KEY。eq_ref可以用于使用= 操作符比較的帶索引的列。比較值可以為常量或一個使用在該表前面所讀取的表的列的表達式。
  4. ref:對于每個來自于前面的表的行組合,所有有匹配索引值的行將從這張表中讀取。如果聯接只使用鍵的最左邊的前綴,或如果鍵不是UNIQUE或PRIMARY KEY(換句話說,如果聯接不能基于關鍵字選擇單個行的話),則使用ref。如果使用的鍵僅僅匹配少量行,該聯接類型是不錯的。ref可以用于使用=或<=>操作符的帶索引的列。
  5. ref_or_null:該聯接類型如同ref,但是添加了MySQL可以專門搜索包含NULL值的行。在解決子查詢中經常使用該聯接類型的優化。
  6. index_merge:該聯接類型表示使用了索引合并優化方法。在這種情況下,key列包含了使用的索引的清單,key_len包含了使用的索引的最長的關鍵元素。
  7. unique_subquery:該類型替換了下面形式的IN子查詢的ref:value IN (SELECT primary_key FROMsingle_table WHERE some_expr);unique_subquery是一個索引查找函數,可以完全替換子查詢,效率更高。
  8. index_subquery:該聯接類型類似于unique_subquery??梢蕴鎿QIN子查詢,但只適合下列形式的子查詢中的非唯一索引:value IN (SELECT key_column FROM single_table WHERE some_expr)
  9. range:只檢索給定范圍的行,使用一個索引來選擇行。key列顯示使用了哪個索引。key_len包含所使用索引的最長關鍵元素。在該類型中ref列為NULL。當使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操作符,用常量比較關鍵字列時,可以使用range
  10. index:該聯接類型與ALL相同,除了只有索引樹被掃描。這通常比ALL快,因為索引文件通常比數據文件小。
  11. all:對于每個來自于先前的表的行組合,進行完整的表掃描。如果表是第一個沒標記const的表,這通常不好,并且通常在它情況下很差。通??梢栽黾痈嗟乃饕灰褂肁LL,使得行能基于前面的表中的常數值或列值被檢索出。

possible_keys:possible_keys列指出MySQL能使用哪個索引在該表中找到行。注意,該列完全獨立于EXPLAIN輸出所示的表的次序。這意味著在possible_keys中的某些鍵實際上不能按生成的表次序使用。

key:key列顯示MySQL實際決定使用的鍵(索引)。如果沒有選擇索引,鍵是NULL。要想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

key_len:key_len列顯示MySQL決定使用的鍵長度。如果鍵是NULL,則長度為NULL。注意通過key_len值我們可以確定MySQL將實際使用一個多部關鍵字的幾個部分。

ref:ref列顯示使用哪個列或常數與key一起從表中選擇行。

rows:rows列顯示MySQL認為它執行查詢時必須檢查的行數。

Extra:該列包含MySQL解決查詢的詳細信息。

  1. Distinct:MySQL發現第1個匹配行后,停止為當前的行組合搜索更多的行。
  2. Not exists:MySQL能夠對查詢進行LEFT JOIN優化,發現1個匹配LEFT JOIN標準的行后,不再為前面的的行組合在該表內檢查更多的行。
  3. range checked for each record (index map: #):MySQL沒有發現好的可以使用的索引,但發現如果來自前面的表的列值已知,可能部分索引可以使用。對前面的表的每個行組合,MySQL檢查是否可以使用range或index_merge訪問方法來索取行。
  4. Using filesort:MySQL需要額外的一次傳遞,以找出如何按排序順序檢索行。通過根據聯接類型瀏覽所有行并為所有匹配WHERE子句的行保存排序關鍵字和行的指針來完成排序。然后關鍵字被排序,并按排序順序檢索行。
  5. Using index:從只使用索引樹中的信息而不需要進一步搜索讀取實際的行來檢索表中的列信息。當查詢只使用作為單一索引一部分的列時,可以使用該策略。
  6. Using temporary:為了解決查詢,MySQL需要創建一個臨時表來容納結果。典型情況如查詢包含可以按不同情況列出列的GROUP BY和ORDER BY子句時。
  7. Using where:WHERE子句用于限制哪一個行匹配下一個表或發送到客戶。除非你專門從表中索取或檢查所有行,如果Extra值不為Using where并且表聯接類型為ALL或index,查詢可能會有一些錯誤。
  8. Using sort_union(...), Using union(...), Using intersect(...):這些函數說明如何為index_merge聯接類型合并索引掃描。
  9. Using index for group-by:類似于訪問表的Using index方式,Using index for group-by表示MySQL發現了一個索引,可以用來查詢GROUP BY或DISTINCT查詢的所有列,而不要額外搜索硬盤訪問實際的表。并且,按最有效的方式使用索引,以便對于每個組,只讀取少量索引條目。

單列索引的使用

準備工作

給id加主鍵索引:

再分別給name、city、country、address加上普通索引:

注:以上五個索引都是單列索引。

使用情況

只涉及到其中的一個字段時,都能使用到索引(以e_name為例):

注:模糊查詢時,%如果在前面,那么不會使用索引。

涉及到多個索引字段時,如果這些索引字段中,存在主鍵索引,那么只會使用該索引(即:MYSQL優化器會選出并先執行最“嚴”的索引):

提示:possible_key中,只是SQL語句里涉及到的索引;key中才是實際上執行查詢操作時使用到了的索引。

涉及到多個索引字段時,如果這些索引字段中,不存在主鍵索引的話,那么就會使用該使用的索引(注:如果通過其中的部分索引就能準確定位的話,那么其余的索引就不再被使用):

注:多個索引時,先使用哪個索引后使用哪個索引,是由MySQL的優化器經過一些列計算后作出的抉擇。

當對索引字段進行?>,?<,>=, <=,not in,between …… and ……,函數(索引字段),like模糊查詢%在字段前時,不會使用該索引

注:這里對e_age字段進行了 “<” ,所以實際查詢時,并沒有使用e_age的索引。

提示:在實際使用時,如果涉及到多列,我們一般都不會將這些列一 一創建為單列索引,而是將這些列創建為組合索引。


組合索引的使用

最左原則

? ? ? ?假設組合索引為:a,b,c的話;那么當SQL中對應有:aa,ba,b,c的時候,可稱為完全滿足最左原則;當SQL中查詢條件對應只有a,c的時候,可稱為部分滿足最左原則;當SQL中沒有a的時候,可稱為不滿足最左原則。

注:MySQL5.7開始,會自動優化,如:會把c,b,a優化為a,b,c使之完全遵循最左原則;會把c,a優化為a,c使之部
? ? ? ?分遵循最左原則
。即:SQL語句中的對應條件的先后順序無關。

準備工作

創建了組合索引:e_name,e_age,e_country,e_city。

使用情況

完全滿足最左原則

注:與條件的先后無關(這是因為MYSQL5.7開始,對索引全排列有優化,會自動優化為按組合索引的順序進行查詢),
? ? ? ?即:下面這樣的話,也是會完整的走組合索引的:

部分滿足最左原則

注:此SQL中,只有e_name和e_country滿足部分最左原則(e_name滿足),所以到e_name字段時會走組合所以,但是
? ? ? ?只會走到e_name那里,到e_country時就不會使用組合索引了。

不滿足最左原則

滿足(部分滿足)最左原則的字段里,有字段不滿足“索引”自身的使用規范

說明:如果SQL語句里的字段里,滿足了最左原則,但是不滿足“索引”自身的使用規范,那么組合索引走到這里之后,
? ? ? ? ? ?不會再往下走了。

如圖所示:由于e_age字段使用了“>”符號,不符合“索引”自身的使用規范,那么當“e_name”走完組合索引后,
? ? ? ? ? ? ? ? ??走到“e_age”時,該字段及其后面的字段不會再走組合索引了。


【補充】使用組合索引時,不遵循最左原則仍然會走索引的特殊種情況

相關概念

聚集索引與非聚集索引

? ? ? ?每個InnoDB表具有一個特殊的索引稱為聚簇索引(也叫聚集索引,聚類索引,簇集索引)。如果表上定義有主鍵,該主鍵索引就是聚簇索引。如果未定義主鍵,MySQL取第一個唯一索引(unique)而且只含非空列(NOT NULL)作為主鍵,InnoDB使用它作為聚簇索引。如果沒有這樣的列,InnoDB就自己產生一個這樣的ID值,它有六個字節,而且是隱藏的,使其作為聚簇索引。

? ? ? ?表中的聚簇索引(clustered index )就是一級索引,除此之外,表上的其他非聚簇索引都是二級索引,又叫輔助索引(secondary indexes)。

回表

? ? ? ? 當二級索引無法直接查詢到(SQL中select需要的所有)列的數據時,會通過二級索引查詢到聚簇索引(即:一級索引)后,再根據(聚集索引)查詢到(二級索引中無法提供)的數據,這種通過二級索引查詢出一級索引,再通過一級索引查詢(二級索引中無法提供的)數據的過程,就叫做回表。

當無需回表時,不遵循最左原則也是會走組合索引

如,現有表:

id是主鍵,其余三個字段組成聯合索引:

當不需要進行回表時,即便我們的SQL不滿足組合索引最左原則,也會走組合索引的,如:

? ? ? ? 這里where后直接是gender時, 是不遵循組合索引的最左原則的,但是查詢計劃顯示使用了索引的。這是因為: 對這張表進行select *,相當于進行select id,name,age,gender,其中,id是主鍵(一級索引),name、age、gender是組合索引(二級索引),這里查詢時,能直接從索引中拿到想要查詢的所有列的數據,是不需要回表查詢的,所以這里哪怕sql寫法上不遵循最左原則,但是仍然是會走索引的。

如果這個時候,我們加一個普通的motto字段:

?

使用相同的SQL進行查詢,可看到:

?

? ? ? ? 此時進行select *,相當于進行select id,name,age,gender,motto,其中motto字段是從索引(一級索引、二級索引)里面獲取不到數據的,是肯定需要回表的。而查詢條件又不遵循最左原則,所以不會走組合索引。

注:其它情況下,只有(完全或部分)遵循了最左原則,才會走組合索引。


^_^ 如有不當之處,歡迎指正

^_^ 參考鏈接
? ? ? ? ? ? ? https://www.cnblogs.com/DreamDrive/p/7752960.html
? ? ? ? ? ? ? https://www.cnblogs.com/tommy-huang/p/4317305.html
? ? ? ? ? ? ? https://blog.csdn.net/linjpg/article/details/56054994
? ? ? ? ? ? ? https://www.jb51.net/article/118371.html

? ? ? ? ? ? ??https://www.csdn.net/gather_2a/MtTaMgwsNzY0OS1ibG9n.html

^_^ 如涉及侵權問題,請及時聯系我

^_^ 本文已經被收錄進《程序員成長筆記(二)》,筆者JustryDeng

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
金玫玫床戏