前面幾篇文章詳細介紹了MySQL數據庫的DML,DDL,DCL,DQL經常使用操做,本篇文章將介紹MySQL中一塊對於開發和維護都比較重要的內容--MySQL索引的應用!
一、索引的做用
(1)若是索引爲惟一索引,能夠保證數據庫中每一行數據的惟一性
(2)索引若是建立的合適,會大幅度提升數據庫的查詢性能,這也是索引最大的做用
(3)索引可以使得在查詢過程當中,使用到數據庫的查詢優化器,極大提升系統的性能mysql
二、索引的分類
(1)按照數據結構和使用的算法劃分:算法
B+Tree索引sql
內部實現採用了B+Tree數據結構,數據所有存在葉子節點上。本質上是一棵平衡排序樹,由二叉樹進化而來,各個葉結點由指針相連,按照從左到右的順序讀取葉子結點上的數據,會獲得一個有序的數列。
Hash索引數據庫
內部實現採用了Hash算法,數據的保存方式爲一對一,一個鍵對應一條惟一的記錄,相似於Redis或者Memcached中的K-V存儲結構。
R-Tree索引服務器
內部實現採用了R-Tree數據結構,R-Tree是一種空間索引的數據結構,它是B樹向多維空間發展的另一種形式,在地理位置測繪領域有所應用,其餘場景幾乎沒有應用,瞭解便可。
(2)按照類型劃分:數據結構
普通索引性能
普通索引是一種最基本的索引,只是爲了提升數據的查詢效率,也是開發中使用比較多的一種索引,容許索引列重複。
主鍵索引測試
用來惟一標識數據庫中的一條記錄,經常使用於保證數據庫中記錄的參照完整性,既不可爲空,也不能重複。
惟一索引優化
用來惟一標識數據庫中的一條記錄,可是與主鍵索引稍有不一樣,惟一索引容許索引列的值爲空,可是不容許索引列的值發生重複。
聯合索引/組合索引搜索引擎
指在數據庫表中的某幾個字段上同時創建的索引,即:這個索引會關聯不止一個列。使用的時候須要特別注意,這種索引遵循最左前綴匹配規則,在下面的索引使用中會詳細介紹。
全文索引
用來完成某一段文字中的關鍵字查找,能夠簡單理解爲like的增強版,不過使用方法和like不一樣,全文索引比較像一個搜索引擎。它目前支持的數據類型有:char,varchar和text類型。
三、索引的建立和查看
(1)索引的建立
語法:
方法一:使用CREATE INDEX方法
CREATE <unique> INDEX index_name ON table_name(<field1,field2,...>);
方法二:使用修改表結構的方法
ALTER TABLE table_name ADD <unique> INDEX index_name ON table_name(<field1,field2,...>);
方法三:在建立表的時候指定
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列上創建普通索引:
方法一:建立表的時候指定
mysql> USE test; mysql> 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建立索引,此種方式不能建立主鍵索引
mysql> USE test; #建立表的時候先不指定索引: mysql> 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命令建立表上的索引 mysql> CREATE UNIQUE INDEX user_no_ind ON t_user(user_no); mysql> mysql> CREATE INDEX name_pass_ind ON t_user(login_name,login_pass); mysql> CREATE INDEX phone_ind ON t_user(phone);
方法三:使用ALTER TABLE修改表結構的方式建立索引
mysql> USE test; #建立表結構 mysql> 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命令建立索引 mysql> ALTER TABLE t_user ADD PRIMARY KEY(id); mysql> ALTER TABLE t_user ADD UNIQUE INDEX user_no_ind(user_no); mysql> ALTER TABLE t_user ADD INDEX name_pass_ind(login_name,login_pass); mysql> ALTER TABLE t_user ADD INDEX phone_ind(phone);
示例2:建立一張帖子內容表,並在帖子內容列建立全文索引
方法一:建立表結構的時候指定索引
mysql> USE test; mysql> 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方式建立全文索引
mysql> USE test; mysql> 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; #添加全文索引 mysql> CREATE FULLTEXT INDEX note_ind ON t_note(note_content);
方法三:使用ALTER TABLE修改表結構的方式建立全文索引
mysql> USE test; mysql> 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; #添加全文索引 mysql> ALTER TABLE t_note ADD FULLTEXT note_ind(note_content);
(2)索引信息的查看
語法:
SHOW INDEX FROM table_name <WHERE key_name = ''>; SHOW INDEXES FROM table_name <WHERE key_name=''>; 注意:SHOW後面能夠爲INDEX或者INDEXES,可使用WHERE條件根據索引名稱查看索引信息
示例:查看t_user表上所建立的索引
mysql> USE t_user; mysql> 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"命令去更新下這個值,有利於優化器對索引使用的判斷。
四、索引的修改和刪除
(1)索引的刪除
語法:
DROP INDEX index_name ON table_name;
示例:
示例1:刪除t_user表中phone列上的phone_ind索引
mysql> USE test; mysql> DROP INDEX phone_ind ON t_user; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0
示例2:刪除t_user表中的id列上的主鍵索引
mysql> USE test; mysql> ALTER TABLE t_user DROP PRIMARY KEY; Query OK, 0 rows affected (0.19 sec) Records: 0 Duplicates: 0 Warnings: 0
(2)索引的修改
索引的修改過程實際上是先刪除索引,在從新建立索引,能夠按照上述的刪除索引和建立索引步驟完成。
五、索引使用注意事項
(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';
六、執行計劃查看
語法:
EXPLAIN <select statement>
示例:
示例1:查看t_user表上面的id列查詢執行計劃:
mysql> USER test; mysql> 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:表示查詢類型,有如下幾種值: SIMPLE:表示該查詢爲一個簡單查詢,如上述的根據id查詢就是一個簡單查詢 SUBQUERY:表示該查詢是WHERE字句中的子查詢 DERIVED:表示該查詢是FROM字句中的子查詢 PRIMARY:表示一個負責嵌套查詢最外層的查詢 UNION:表示UNION查詢的第二個SELECT子查詢 UNION RESULT:表示該結果是從UNION表中查詢出的結果 table:表示查詢時所關聯的表 type:表示關聯類型或者訪問類型,常見的幾種值以下: ALL:表示未使用索引,掃描全表 index:表示掃描全部的索引,經過掃描索引樹去定位全部待查詢數據 range:表示按照索引的範圍掃描,即:從某索引的某個位置開始,到索引的另一個位置結束,找出這個範圍內每一個索引對應的數據,範圍查詢會出現range ref:非惟一索引掃描,MySQL將返回匹配這個索引的全部行 eq_ref:惟一索引掃描,即:經過該索引只能定位到表中的一條數據,主鍵索引和惟一索引屬於這種類型 possible_keys:查詢優化器可能使用到的索引,可是不必定使用 key:查詢優化器真正使用的索引 key_len:使用的索引長度,好比:在某個比較長的列上,一般只會給前多少個字符建立索引,這個長度就表示索引字符的長度 ref:表示表的鏈接匹配條件中,哪些列或者常量被用於查找索引列上的值。能夠理解爲若是要完成這個查詢,須要關聯其餘表中的哪一個列或者常量 rows:要查詢到目標數據,須要掃描的行數 Extra:其餘額外信息,常見的有如下幾種: Using Where:表示查詢結果須要在存儲引擎層經過Where條件完成過濾 Using index:表示該Where條件的查詢使用到了覆蓋索引,即:該索引包括了知足查詢全部數據的需求; Using tempory:表示該查詢使用到了臨時表來存儲中間結果集,一般在排序或者分組查詢中會出現 Using filesort:表示該查詢使用到了寫磁盤的方式來存儲結果集,出現這種狀況,表示查詢性能極差,已經發生了磁盤IO
以上的查詢計劃中主要關注的列:
type:查詢類型,該列出現了ALL的查詢類型,就表示查詢語句有問題,須要根據索引使用的注意事項來排查 key:表示查詢是否用到了索引,若是該列爲NULL,表示索引未起到實際做用 rows:查詢行數,若是特別大,和表中的數據條數相差不大,則表示索引利用率特別低,須要優化索引 Extra:若是發現有Using filesort,表示索引的效率特別差,已經發生了磁盤IO,須要排查對應的語句和索引使用狀況
示例2:查詢t_user表中id大於4的記錄,觀察其執行計劃
mysql> USE test; mysql> 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列的索引使用狀況
mysql> USE test; mysql> 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最大的記錄的執行計劃
mysql> USE test; mysql> 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不會使用到索引。
七、索引優化
(1)對於主鍵索引,最好採用整型數字的格式,由於在普通索引中,索引樹的葉子節點上存儲的是主鍵索引的值,這個值若是過大,會致使普通索引會變的特別大
(2)在更新頻繁的列上最好少建立索引,由於更新須要維護索引樹,而這個維護過程是很耗時的
(3)建立聯合索引時,應該考慮哪些組合列上的查詢需求最大,從而肯定聯合索引的順序,由於聯合索引有左前綴規則
(4)索引並非越多越好,索引若是過多,會致使磁盤大量浪費,並且在更新這些查詢較少的列時,會產生很大的IO操做,形成服務器資源浪費
(5)索引能夠在後期經過監控MySQL數據庫中的慢SQL再來優化和添加。最好的辦法仍是開始就考慮周全,建立好索引
(6)添加索引的時候,須要注意是否有慢SQL,若是有慢SQL,會阻塞索引的添加操做,一直處於等待中
至此,MySQL索引相關內容介紹完畢,理論性的東西較多,上述講解理論的同時,也舉了一些實際例子。內容比較豐富,若有不清楚的能夠留言!下篇文章將開始介紹MySQL的維護篇多實例搭建,若有其它須要介紹的也能夠留言,歡迎你們轉發評論!
後續更多文章將更新在我的小站上,歡迎查看。
另外提供一些優秀的IT視頻資料,可免費下載!如須要請查看https://www.592xuexi.com