MySQL索引優化

什麼是索引:mysql

索引(Index)是幫助MySQL高效獲取數據的數據結構~算法

索引的目的在於提升查詢效率,能夠類比字典~
在數據以外,數據庫系統還維護着知足特定查找算法的數據結構(BTREE),這些數據結構以某種方式指向數據~
這樣就能夠在這些數據結構上實現快速查找,這種數據結構就是索引。下圖就是一種可能的索引方式示例:

左邊是數據表,一共有兩列七條數據,最左邊是數據記錄的物理地址~sql

就像我想找Col2=89的記錄,比較一次就找到了,若是全表掃描的話就會很慢~數據庫

爲了加快Col2的查找,能夠維護一個右邊所示的二叉查找樹,每一個節點分別包含索引鍵值和一個指向對應數據記錄的物理地址的指針,這樣就能夠運用二叉查找在必定的複雜度內獲取到相應的數據。從而快速的檢索出符合條件的記錄。數據結構

通常來講索引自己也很大,不可能所有存儲在內存中,所以索引每每以索引文件的形式存儲在磁盤上。
咱們日常所說的索引,若是沒有特別指明,都是指B樹(多路搜索樹,不必定是二叉樹)結構組織的索引。其中彙集索引,次要索引,覆蓋索引,複合索引,前綴索引,惟一索引
默認都是使用B+樹索引,統稱索引。固然除了B+樹這種類型的索引以外還有哈希索引等。函數

索引的好處:性能

第一,經過建立惟一性索引,能夠保證數據庫表中每一行數據的惟一性。mysql索引

第二,能夠大大加快數據的檢索速度,這也是建立索引的最主要的緣由。優化

第三,能夠加速表和表之間的鏈接,特別是在實現數據的參考完整性方面特別有意義。spa

第四,在使用分組和排序子句進行數據檢索時,一樣能夠顯著減小查詢中分組和排序的時間。

第五,經過使用索引,能夠在查詢的過程當中,使用優化隱藏器,提升系統的性能。 

也許會有人要問:增長索引有如此多的優勢,爲何不對錶中的每個列建立一個索引呢?由於,增長索引也有許多不利的方面。

索引帶來的問題:

第一,建立索引和維護索引要耗費時間,這種時間隨着數據量的增長而增長。

第二,索引須要佔物理空間,除了數據表佔數據空間以外,每個索引還要佔必定的物理空間,若是要創建聚簇索引,那麼須要的空間就會更大。

第三,當對錶中的數據進行增長、刪除和修改的時候,索引也要動態的維護,這樣就下降了數據的維護速度。

索引是創建在數據庫表中的某些列的上面。在建立索引的時候,應該考慮在哪些列上能夠建立索引,在哪些列上不能建立索引。

通常來講,應該在這些列上建立索引:

1.主鍵自動創建惟一索引

2.頻繁做爲查詢條件的字段應該創建索引,能夠加快搜索的速度

3.在常常用在鏈接的列上,這些列主要是一些外鍵,能夠加快鏈接的速度;

4.在常常須要根據範圍進行搜索的列上建立索引,由於索引已經排序,其指定的範圍是連續的;

5.在常常須要排序的列上建立索引,由於索引已經排序,這樣查詢能夠利用索引的排序,加快排序查詢時間;

一樣,對於有些列不該該建立索引。通常來講,不該該建立索引的的這些列具備下列特色:

1,對於那些在查詢中不多使用或者參考的列不該該建立索引。這是由於,既然這些列不多使用到,所以有索引或者無索引,並不能提升查詢速度。相反,因爲增長了索引,反而下降了系統的維護速度和增大了空間需求。

2,對於那些只有不多數據值的列也不該該增長索引。這是由於,因爲這些列的取值不多,例如人事表的性別列,在查詢的結果中,結果集的數據行佔了表中數據行的很大比例,即須要在表中搜索的數據行的比例很大。增長索引,並不能明顯加快檢索速度。

3,對於那些定義爲text, image和bit數據類型的列不該該增長索引。這是由於,這些列的數據量要麼至關大,要麼取值不多。

4,當修改性能遠遠大於檢索性能時,不該該建立索引。這是由於,修改性能和檢索性能是互相矛盾的。當增長索引時,會提升檢索性能,可是會下降修改性能。當減小索引時,會提升修改性能,下降檢索性能。所以,當修改性能遠遠大於檢索性能時,不該該建立索引。

1.應該避免的索引失效的狀況

CREATE TABLE staffs(
id INT(10) PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(24) NOT NULL DEFAULT '' COMMENT '姓名',
age INT(10) NOT NULL DEFAULT 0 COMMENT '年齡',
pos VARCHAR(24) NOT NULL DEFAULT '' COMMENT '職位',
add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入職時間'
 )CHARSET utf8 COMMENT '員工記錄表';
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('z3',22,'manager',NOW());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('July',23,'dev',NOW());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('2000',23,'dev',NOW());

ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos(NAME,age,pos);
mysql> show index from staffs;
+--------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table  | Non_unique | Key_name              | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| staffs |          0 | PRIMARY               |            1 | id          | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
| staffs |          1 | idx_staffs_nameAgePos |            1 | NAME        | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
| staffs |          1 | idx_staffs_nameAgePos |            2 | age         | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
| staffs |          1 | idx_staffs_nameAgePos |            3 | pos         | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
+--------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.00 sec)

規則1:全值匹配我最愛

mysql> explain select * from staffs where name='July';
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
1 row in set (0.01 sec)

mysql> explain select * from staffs where name='July' and age=23;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref         | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 78      | const,const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------+------+-----------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name='July' and age=23 and pos='dev';
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref               | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 152     | const,const,const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
1 row in set (0.00 sec)

第三種效果最好~ 用到了咱們建立的索引,下面再看一種狀況

mysql> explain select * from staffs where age=23 and pos='dev';
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.01 sec)

mysql> explain select * from staffs where pos='dev';
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

全表掃描!!咱們看到,索引居然失效了

mysql> explain select * from staffs where name='July';
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

單獨使用name就不會使索引失效,由此引入規則2

規則2:最佳左前綴法則,即若是索引了多列,要遵照最佳左前綴法則,即查詢必須從索引的最左前列開始,而且不跳過索引中的列~

ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos(name,age,pos);

索引的最左前列是name,name至關於火車頭,沒有火車頭,搞毛~因此索引的第一個字段不能丟失

再來看一種狀況

mysql> explain select * from staffs where name='July' and pos='dev';
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

只有一個const,這和只用name是同樣的,違背了什麼原則呢?不跳過索引中的列

 

mysql> explain select * from staffs where name='July' and age=23;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref         | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 78      | const,const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------+------+-----------------------+
1 row in set (0.00 sec)

 

以上兩個對比一下便可~

 

規則3.不要在索引列上作任何操做(計算,函數,類型轉換),會致使索引失效而轉向全表掃描~

mysql> select * from staffs where name='July';
+----+------+-----+-----+---------------------+
| id | NAME | age | pos | add_time            |
+----+------+-----+-----+---------------------+
|  2 | July |  23 | dev | 2017-03-17 23:14:23 |
+----+------+-----+-----+---------------------+
1 row in set (0.00 sec)

mysql> select * from staffs where left(name,4)='July';
+----+------+-----+-----+---------------------+
| id | NAME | age | pos | add_time            |
+----+------+-----+-----+---------------------+
|  2 | July |  23 | dev | 2017-03-17 23:14:23 |
+----+------+-----+-----+---------------------+
1 row in set (0.04 sec)

mysql> explain select * from staffs where name='July';
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where left(name,4)='July';
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

規則4.存儲引擎不能使用索引中範圍條件右邊的列

mysql> explain select * from staffs where name='July' and age=23 and pos='dev';
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref               | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 152     | const,const,const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name='July' and age>23 and pos='dev';
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
| id | select_type | table  | type  | possible_keys         | key                   | key_len | ref  | rows | Extra                 |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
|  1 | SIMPLE      | staffs | range | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 78      | NULL |    1 | Using index condition |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
1 row in set (0.00 sec)
age>23致使後面的pos索引失效

規則5.儘可能使用覆蓋索引(只訪問索引的查詢(索引列和查詢列一致)),減小select *

mysql> explain select * from staffs where name='July' and age=23 and pos='dev';
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref               | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 152     | const,const,const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
1 row in set (0.00 sec)

mysql> explain select name,age,pos from staffs where name='July' and age=23 and pos='dev';
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+--------------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref               | rows | Extra                    |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+--------------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 152     | const,const,const |    1 | Using where; Using index |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+--------------------------+
1 row in set (0.00 sec)

mysql> explain select name,age,pos from staffs where name='July' and age>23 and pos='dev';
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+--------------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                    |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using where; Using index |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name='July' and age>23 and pos='dev';
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
| id | select_type | table  | type  | possible_keys         | key                   | key_len | ref  | rows | Extra                 |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
|  1 | SIMPLE      | staffs | range | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 78      | NULL |    1 | Using index condition |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
1 row in set (0.00 sec)

規則6.MySQL中在使用不等於(!=或者<>)的時候沒法使用索引致使全表掃描

mysql> explain select * from staffs where name='July';
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name!='July';
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys         | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name<>'July';
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys         | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

規則7.is null,is not null也沒法使用索引

mysql> explain select * from staffs where name is null;
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra            |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
|  1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name is not null;
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys         | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

因此建表的時候字段不能爲null

規則8.like以通配符開頭('%abc')mysql索引失效會變成全表掃描的操做~

mysql> explain select * from staffs where name like '%July%';
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from staffs where name like 'July%';
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
| id | select_type | table  | type  | possible_keys         | key                   | key_len | ref  | rows | Extra                 |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
|  1 | SIMPLE      | staffs | range | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | NULL |    1 | Using index condition |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
1 row in set (0.00 sec)

問題:解決like%字符串%時索引不被使用的問題~

使用覆蓋索引~

規則9.字符串不加單引號索引失效

mysql> select * from staffs where name=2000;
+----+------+-----+-----+---------------------+
| id | NAME | age | pos | add_time            |
+----+------+-----+-----+---------------------+
|  3 | 2000 |  23 | dev | 2017-03-17 23:14:23 |
+----+------+-----+-----+---------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where name='2000';
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name=2000;
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys         | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

底層作了隱式的數據轉換,將2000轉換成'2000',致使索引失效

規則10.少用or,用它來鏈接時索引會失效

mysql> explain select * from staffs where name='July' or name='z3';
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys         | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
相關文章
相關標籤/搜索