MySQL索引實戰

1、索引的作用

(1)如果索引為唯一索引,可以保證數據庫中每一行數據的唯一性

(2)索引如果創建的合適,會大幅度提高數據庫的查詢性能,這也是索引最大的作用

(3)索引能夠使得在查詢過程中,使用到數據庫的查詢優化器,極大提高系統的性能

2、索引的分類

(1)按照數據結構和使用的算法劃分:

B+Tree索引:

內部實現採用了B+Tree數據結構,數據全部存在葉子節點上。本質上是一棵平衡排序樹,由二叉樹進化而來,各個葉結點由指針相連,按照從左到右的順序讀取葉子結點上的數據,會得到一個有序的數列。

—————————————

Hash索引:

內部實現採用了Hash算法,數據的保存方式為一對一,一個鍵對應一條唯一的記錄,類似於Redis或者Memcached中的K-V存儲結構。

—————————————

R-Tree索引:

內部實現採用了R-Tree數據結構,R-Tree是一種空間索引的數據結構,它是B樹向多維空間發展的另外一種形式,在地理位置測繪領域有所應用,其他場景幾乎沒有應用,瞭解即可。

(2)按照類型劃分:

普通索引:

普通索引是一種最基本的索引,只是為了提高數據的查詢效率,也是開發中使用比較多的一種索引,允許索引列重複。

—————————————

主鍵索引:

用來唯一標識數據庫中的一條記錄,常用於保證數據庫中記錄的參照完整性,既不可為空,也不能重複。

—————————————

唯一索引:

用來唯一標識數據庫中的一條記錄,但是與主鍵索引稍有不同,唯一索引允許索引列的值為空,但是不允許索引列的值發生重複。

—————————————

聯合索引/組合索引:

指在數據庫表中的某幾個字段上同時建立的索引,即:這個索引會關聯不止一個列。使用的時候需要特別注意,這種索引遵循最左前綴匹配規則,在下面的索引使用中會詳細介紹。

—————————————

全文索引:

用來完成某一段文字中的關鍵字查找,可以簡單理解為like的加強版,不過使用方法和like不同,全文索引比較像一個搜索引擎。它目前支持的數據類型有:char,varchar和text類型。

3、索引的創建和查看

(1)索引的創建

語法:

方法一:使用CREATE INDEX方法

CREATE INDEX index_name ON table_name();

方法二:使用修改表結構的方法

ALTER TABLE table_name ADD INDEX index_name ON table_name();

方法三:在創建表的時候指定

CREATE TABLE table_name (
 field1 INT NOT NULL AUTO_INCREMENT,
 field2 INT ,
 field3 INT ,
 PRIMARY KEY(field_name),
 UNIQUE index_name(field(len)),
 INDEX index_name(field(len))
);

示例:

示例1:創建一張t_user測試表,字段包含:[id(主鍵),user_no(用戶編號),login_name(登錄名稱),login_pass(登錄密碼),phone(手機號)],要求:id作為主鍵,user_no列上建立唯一索引,login_name和login_pass兩個列上建立聯合索引,phone列上建立普通索引:

方法一:創建表的時候指定

CREATE TABLE t_user(
 id INT NOT NULL AUTO_INCREMENT,
 user_no VARCHAR(30) NOT NULL,
 login_name VARCHAR(50) NOT NULL,
 login_pass VARCHAR(50) NOT NULL,
 phone VARCHAR(15) NOT NULL,
 PRIMARY KEY(id), #主鍵索引
 UNIQUE user_no_ind(user_no), #唯一索引
 INDEX name_pass_ind(login_name,login_pass), #聯合索引
 INDEX phone_ind(phone) #普通索引
)ENGINE = InnoDB DEFAULT CHARSET = UTF8;

方法二:使用CREATE INDEX創建索引,此種方式不能創建主鍵索引

#創建表的時候先不指定索引:
CREATE TABLE t_user(
 id INT NOT NULL AUTO_INCREMENT,
 user_no VARCHAR(30) NOT NULL,
 login_name VARCHAR(50) NOT NULL,
 login_pass VARCHAR(50) NOT NULL,
 phone VARCHAR(15) NOT NULL,
 PRIMARY KEY (id)
) ENGINE = InnoDB DEFAULT CHARSET = UTF8;

#使用CREATE INDEX命令創建表上的索引

CREATE UNIQUE INDEX user_no_ind ON t_user(user_no);

CREATE INDEX name_pass_ind ON t_user(login_name,login_pass);

CREATE INDEX phone_ind ON t_user(phone);

方法三:使用ALTER TABLE修改表結構的方式創建索引

#創建表結構
CREATE TABLE t_user(
 id INT NOT NULL,
 user_no VARCHAR(30) NOT NULL,
 login_name VARCHAR(50) NOT NULL,
 login_pass VARCHAR(50) NOT NULL,
 phone VARCHAR(15) NOT NULL
) ENGINE = InnoDB DEFAULT CHARSET = UTF8;

#使用ALTER TABLE命令創建索引

ALTER TABLE t_user ADD PRIMARY KEY(id);

ALTER TABLE t_user ADD UNIQUE INDEX user_no_ind(user_no);

ALTER TABLE t_user ADD INDEX name_pass_ind(login_name,login_pass);

ALTER TABLE t_user ADD INDEX phone_ind(phone);

示例2:創建一張帖子內容表,並在帖子內容列創建全文索引

方法一:創建表結構的時候指定索引

CREATE TABLE t_note(
 id BIGINT NOT NULL AUTO_INCREMENT,
 note_content TEXT NOT NULL,
 create_time DATETIME,
 PRIMARY KEY(id),
 FULLTEXT(note_content) #添加全文索引
) ENGINE = InnoDB DEFAULT CHARSET = UTF8;
 

方法二:使用CREATE INDEX方式創建全文索引

CREATE TABLE t_note(
 id BIGINT NOT NULL AUTO_INCREMENT,
 note_content TEXT NOT NULL,
 create_time DATETIME,
 PRIMARY KEY(id)
) ENGINE = InnoDB DEFAULT CHARSET = UTF8;

#添加全文索引

CREATE FULLTEXT INDEX note_ind ON t_note(note_content);

方法三:使用ALTER TABLE修改表結構的方式創建全文索引

CREATE TABLE t_note(
 id BIGINT NOT NULL AUTO_INCREMENT,
 note_content TEXT NOT NULL,
 create_time DATETIME,
 PRIMARY KEY(id)
) ENGINE = InnoDB DEFAULT CHARSET = UTF8;

#添加全文索引

ALTER TABLE t_note ADD FULLTEXT note_ind(note_content);

(2)索引信息的查看

語法:

SHOW INDEX FROM table_name ;

SHOW INDEXES FROM table_name ;

注意:SHOW後面可以為INDEX或者INDEXES,可以使用WHERE條件根據索引名稱查看索引信息

示例:查看t_user表上所創建的索引

SHOW INDEXES FROM t_user \G

*************************** 1. row ***************************

Table: t_user

Non_unique: 0

Key_name: PRIMARY

Seq_in_index: 1

Column_name: id

Collation: A

Cardinality: 0

Sub_part: NULL

Packed: NULL

Null:

Index_type: BTREE

Comment:

Index_comment:

…其他行略…

結果字段解釋:

Table:索引所在的表名稱

Non_unique:是否為非唯一的索引,對唯一索引和主鍵索引,此值為0,因為主鍵和唯一鍵必須唯一

Key_name:索引的名稱

Seq_in_index:索引中該列的位置,在

Column_name:索引所在列的列名稱

Collation:列使用哪種方式存儲在索引中,B+數索引總是A,表示排過序的。對於其他的如Hash索引,此處可能為NULL,因為Hash索引並未排序

Cardinality:索引中非胃一直的數目的估計值,通常用於優化器去判斷是否查詢時使本索引

Sub_part:是否只是用了列的一部分作為索引。比如:在某個非常長的字段上的前多少個字符上創建索引的情況

Packed:關鍵字如何被壓縮,Null表示未被壓縮

Null:索引的列中是否含有Null值,主鍵索引,此處為空,表示不含有Null值

Index_type:索引類型,InnoDB存儲引擎,此處為B+樹

Comment:索引列的註釋

Index_comment:索引的註釋

注意:上述字段中,Cardinality字段相對來說比較重要,可以通過該字段來判斷當前的索引是否最優,通常如果索引的利用率比較高的話,這個值會比較接近於表中的記錄數,即:和表中的記錄數接近於1:1,但是這個值並不是實時維護,索引當相差比較大的時候,可以使用”ANALYZE TABLE table_name”命令去更新下這個值,有利於優化器對索引使用的判斷。

4、索引的修改和刪除

(1)索引的刪除

語法:

DROP INDEX index_name ON table_name;

示例:

示例1:刪除t_user表中phone列上的phone_ind索引

DROP INDEX phone_ind ON t_user;

示例2:刪除t_user表中的id列上的主鍵索引

ALTER TABLE t_user DROP PRIMARY KEY;

(2)索引的修改

索引的修改過程其實是先刪除索引,在重新創建索引,可以按照上述的刪除索引和創建索引步驟完成。

5、索引使用注意事項

(1)使用場景

a.業務場景中,讀多寫少的場景

b.SQL查詢場景中,常用於WHERE語句之後的過濾條件;區分度大於80%;WHERE語句之後的過濾字段在過濾時不參與運算;

(2)以下的情況,對於有索引的列,查詢時也不會使用索引

a.當優化器判斷使用索引和不適用索引差別不大時,將不會使用索引,比如:性別列創建的索引

b.查詢條件中發生計算,優化器將不使用索引,比如:WHERE SUBSTR(name,5) = ‘BING’

c.查詢條件中包含了隱士類型轉換,比如:WHERE phone = 13520277199

d.反向查詢不會使用索引,比如:!=,<>,NOT IN,NOT LIKE等,比如:WHERE name != ‘BING’;

e.LIKE的左模糊匹配,將不會使用索引,比如:WHERE name LIKE ‘%BING’,右匹配查詢會走索引;

f.聯合索引中,不滿足左前綴規則,則MySQL不會使用索引。比如:對於name,pass,user_no列的聯合索引,下述情況將不會使用索引:

WHERE pass = ‘value’

WHERE user_no = ‘value’

WHERE pass = ‘123’ AND user_no = ‘123’

而如下的情況將會使用到索引:

WHERE name = ‘bing’

WHERE name = ‘bing’ AND pass = ‘123’;

WHERE name = ‘bing’ AND user_no = ‘021250’;

WHERE pass = ‘123’ AND name = ‘bing’;

WHERE name = ‘bing’ AND pass = ‘123’ AND user_no = ‘021250’;

g.某個帶索引的列和不帶索引的列中間使用OR連接,則帶索引的列也不會使用索引,如:user_no列帶有索引,phone_未帶索引,則:

不會使用索引:WHERE user_no = ‘123’ OR phone = ‘13520277898’

會使用索引:WHERE user_no = ‘123’ AND phone = ‘15265648758’

h.如果在聯合索引中有範圍查詢,如果字段之間使用OR連接,則整個查詢條件不會使用索引,如果字段之間使用AND連接,則從第一個範圍查詢開始之後的條件都不會使用索引

比如:name,score,usre_no列上的聯合索引,則:

不會使用索引:WHERE name = ‘bing’ OR score = 123 OR user_no = ‘02311’;

不會使用索引:WHERE name = ‘bing’ AND score = 123 OR user_no = ‘01231’;

會使用索引:WHERE name = ‘bing’ AND score = 123 AND user_no = ‘021321’;

name列會使用索引,name之後的列不會使用索引:

WHERE name = ‘bing’ AND score > 120 AND user_no = ‘021321’;

6、執行計劃查看

語法:

EXPLAIN

示例:

示例1:查看t_user表上面的id列查詢執行計劃:

EXPLAIN SELECT * FROM t_user WHERE id = 1 \G

********** 1. row ******

id: 1

select_type: SIMPLE

table: t_user

type: const

possible_keys: PRIMARY

key: PRIMARY

key_len: 4

ref: const

rows: 1

Extra: NULL

1 row in set (0.00 sec)

輸出字段說明:

id:查詢編號,如果有多個大小相同的編號,則執行順序為從上到下,如果大小相同,則編號大的先執行

select_type:表示查詢類型,有以下幾種值:

a.SIMPLE:表示該查詢為一個簡單查詢,如上述的根據id查詢就是一個簡單查詢

b.SUBQUERY:表示該查詢是WHERE字句中的子查詢

c.DERIVED:表示該查詢是FROM字句中的子查詢

d.PRIMARY:表示一個負責嵌套查詢最外層的查詢

e.UNION:表示UNION查詢的第二個SELECT子查詢

f.UNION RESULT:表示該結果是從UNION表中查詢出的結果

table:表示查詢時所關聯的表

type:表示關聯類型或者訪問類型,常見的幾種值如下:

ALL:表示未使用索引,掃描全表

index:表示掃描所有的索引,通過掃描索引樹去定位所有待查詢數據

range:表示按照索引的範圍掃描,即:從某索引的某個位置開始,到索引的另外一個位置結束,找出這個範圍內每個索引對應的數據,範圍查詢會出現range

ref:非唯一索引掃描,MySQL將返回匹配這個索引的所有行

eq_ref:唯一索引掃描,即:通過該索引只能定位到表中的一條數據,主鍵索引和唯一索引屬於這種類型

possible_keys:查詢優化器可能使用到的索引,但是不一定使用

key:查詢優化器真正使用的索引

key_len:使用的索引長度,比如:在某個比較長的列上,通常只會給前多少個字符創建索引,這個長度就表示索引字符的長度

ref:表示表的連接匹配條件中,哪些列或者常量被用於查找索引列上的值。可以理解為如果要完成這個查詢,需要關聯其他表中的哪個列或者常量

rows:要查詢到目標數據,需要掃描的行數

Extra:其他額外信息,常見的有以下幾種:

a.Using Where:表示查詢結果需要在存儲引擎層通過Where條件完成過濾

b.Using index:表示該Where條件的查詢使用到了覆蓋索引,即:該索引包括了滿足查詢所有數據的需求;

c.Using tempory:表示該查詢使用到了臨時表來存儲中間結果集,通常在排序或者分組查詢中會出現

d.Using filesort:表示該查詢使用到了寫磁盤的方式來存儲結果集,出現這種情況,表示查詢性能極差,已經發生了磁盤IO

以上的查詢計劃中主要關注的列:

type:查詢類型,該列出現了ALL的查詢類型,就表示查詢語句有問題,需要根據索引使用的注意事項來排查

key:表示查詢是否用到了索引,如果該列為NULL,表示索引未起到實際作用

rows:查詢行數,如果特別大,和表中的數據條數相差不大,則表示索引利用率特別低,需要優化索引

Extra:如果發現有Using filesort,表示索引的效率特別差,已經發生了磁盤IO,需要排查對應的語句和索引使用情況

示例2:查詢t_user表中id大於4的記錄,觀察其執行計劃

EXPLAIN SELECT * FROM t_user WHERE id > 4\G

********* 1. row *********

id: 1

select_type: SIMPLE

table: t_user

type: range

possible_keys: PRIMARY

key: PRIMARY

key_len: 4

ref: NULL

rows: 1

Extra: Using where

1 row in set (0.00 sec)

從上述輸出結果看出:type列為range,possible_keys和key都為PRIMARY,表示該查詢使用到了範圍查詢,而且用到了主鍵索引。

示例3:查看t_user表中login_name列的索引使用情況

EXPLAIN SELECT * FROM t_user WHERE login_name = ‘aaa’ \G

*********** 1. row **********

id: 1

select_type: SIMPLE

table: t_user

type: ref

possible_keys: name_pass_ind

key: name_pass_ind

key_len: 152

ref: const

rows: 1

Extra: Using index condition

1 row in set (0.00 sec)

由結果可以看出,login_name列的條件查詢用到了login_name,login_pass列的聯合索引。

示例4:查看t_user表中id最大的記錄的執行計劃

EXPLAIN SELECT * FROM t_user WHERE id = (SELECT MAX(id) FROM t_user) \G

*********** 1. row ********

id: 1

select_type: PRIMARY

table: t_user

type: const

possible_keys: PRIMARY

key: PRIMARY

key_len: 4

ref: const

rows: 1

Extra: NULL

************ 2. row ********

id: 2

select_type: SUBQUERY

table: NULL

type: NULL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: NULL

Extra: Select tables optimized away

2 rows in set (0.00 sec)

由上述結果可以看出,位於WHERE之後的子查詢的查詢類型為SUBQUERY,直接查詢最大id不會使用到索引。

7、索引優化

(1)對於主鍵索引,最好採用整型數字的格式,因為在普通索引中,索引樹的葉子節點上存儲的是主鍵索引的值,這個值如果過大,會導致普通索引會變的特別大

(2)在更新頻繁的列上最好少創建索引,因為更新需要維護索引樹,而這個維護過程是很耗時的

(3)創建聯合索引時,應該考慮哪些組合列上的查詢需求最大,從而確定聯合索引的順序,因為聯合索引有左前綴規則

(4)索引並不是越多越好,索引如果過多,會導致磁盤大量浪費,而且在更新這些查詢較少的列時,會產生很大的IO操作,造成服務器資源浪費

(5)索引可以在後期通過監控MySQL數據庫中的慢SQL再來優化和添加。最好的辦法還是開始就考慮周全,創建好索引

(6)添加索引的時候,需要注意是否有慢SQL,如果有慢SQL,會阻塞索引的添加操作,一直處於等待中

文章為作者原創,轉載請務必標註出處:https://www.jinnianshizhunian.vip

手機端顯示格式的問題,好多文章發佈之後拍板會混亂,如果需要,可以複製上述作者網址,在PC端查看更多文章。


分享到:


相關文章: