MySQL數據庫之索引的應用

前面幾篇文章詳細介紹了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

相關文章
相關標籤/搜索