MySQL索引的學習和研究

這裏所談論只針對B-Tree類型索引,也是MySQL用的最多最普通的索引。建立索引的時候是按照字面量的順序建立的,這個要特別注意。在B-Tree類型索引中,索引順序是相當重要的。mysql

高性能索引

  • 索引要小而美sql

  • 單列索引數據庫

  • 前綴索引函數

  • 多列索引性能

  • 選擇合適的索引順序.net

  • 覆蓋索引code

  • 使用索引掃描來排序orm

首先構造1億條數據

參照前面寫的文章造數據blog

http://blog.csdn.net/csujiangyu/article/details/51096978排序

索引結構以下

mysql> show index from user;
+-------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name                | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY                 |            1 | id          | A         |    92123632 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | idx_last_first_name_age |            1 | last_name   | A         |      363474 |     NULL | NULL   | YES  | BTREE      |         |               |
| user  |          1 | idx_last_first_name_age |            2 | first_name  | A         |    92123840 |     NULL | NULL   | YES  | BTREE      |         |               |
| user  |          1 | idx_last_first_name_age |            3 | age         | A         |    87360984 |     NULL | NULL   | YES  | BTREE      |         |               |
| user  |          1 | idx_phone               |            1 | phone       | A         |    92123840 |     NULL | NULL   | YES  | BTREE      |         |               |
| user  |          1 | idx_create_time         |            1 | create_time | A         |       88503 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
6 rows in set (0.00 sec)

user表一共有4個索引,包括一個主鍵id,2個單列索引(phone, create_time), 1個多列索引(last_name+first_name+age)

經過explain來查看使用索引狀況

主要關注以下:

  • type:訪問類型,性能從差到好爲:

    ALL -> index -> range -> ref -> eq_ref -> const,system -> NULL

  • index:使用的索引

能夠使用索引的狀況

單列索引全值匹配

這是最簡單的方式

mysql> explain SELECT * from user where phone = '13901986429';
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key       | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | user  | NULL       | ref  | idx_phone     | idx_phone | 14      | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

從type能夠得知訪問類型ref,使用索引key爲idx_phone。

多列索引全值匹配

mysql> explain SELECT * FROM  user where last_name='last_name_110' and first_name='first_name_115' and sex='F';
+----+-------------+-------+------------+------+-------------------------+-------------------------+---------+-------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys           | key                     | key_len | ref         | rows | filtered | Extra       |
+----+-------------+-------+------------+------+-------------------------+-------------------------+---------+-------------+------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | ref  | idx_last_first_name_age | idx_last_first_name_age | 96      | const,const |    1 |    10.00 | Using where |
+----+-------------+-------+------------+------+-------------------------+-------------------------+---------+-------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

前綴索引

若是是單列索引,能夠匹配列的最左前綴

mysql> explain SELECT * from user where phone like '139019864%';
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type  | possible_keys | key       | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | user  | NULL       | range | idx_phone     | idx_phone | 14      | NULL |  100 |   100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.01 sec)

若是是多列索引,能夠從左開始組合索引

mysql> explain SELECT * FROM  user where last_name='last_name_110' and first_name='first_name_115';
+----+-------------+-------+------------+------+-------------------------+-------------------------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys           | key                     | key_len | ref         | rows | filtered | Extra |
+----+-------------+-------+------------+------+-------------------------+-------------------------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | user  | NULL       | ref  | idx_last_first_name_age | idx_last_first_name_age | 96      | const,const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+-------------------------+-------------------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

匹配索引值的範圍

使用between

mysql> explain select * from user where id between 1 and 10;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |   10 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

使用in

mysql> explain select * from user where id in (1, 2, 3);
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    3 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

使用or

mysql> explain select * from user where id=1 or id=10;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    2 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

覆蓋索引

Extra字段爲Using index,能夠看出返回數據只須要查索引便可,不須要回表查其餘字段數據。

mysql> explain select phone from user where phone='13900123456';
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key       | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | ref  | idx_phone     | idx_phone | 14      | const |    1 |   100.00 | Using index |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

不能使用索引的狀況

從type爲ALL能夠看出進行了全表掃描。

沒有使用左前綴索引

phone是索引列,格式爲’%XXX’不能是使用索引

mysql> explain SELECT * from user where phone like '%39019864%';
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9698255 |    11.11 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

沒有采用最左組合索引

last_name + first_name + sex聯合組成了多列索引,採用最左匹配, 只有last_name,last_name + first_name,last_name + first_name + sex才能使用索引。

mysql> explain SELECT * FROM  user where first_name='first_name_115' and sex='F';
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9698255 |     1.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

使用函數

mysql> explain SELECT * FROM  user where left(phone, 11) = '13900123456';
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9698255 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

用索引字段進行計算

mysql> explain select * from user where id + 1 = 2;
+----+-------------+-------+------------+------+---------------+------+---------+------+----------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows     | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+----------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 92123838 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+----------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

or後面沒有使用索引

mysql> explain select * from user where id=1 or age=10;
+----+-------------+-------+------------+------+---------------+------+---------+------+----------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows     | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+----------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | ALL  | PRIMARY       | NULL | NULL    | NULL | 92123838 |    10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+----------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

索引和數據佔用空間的查詢

-- 查看example數據庫總體大小
SELECT CONCAT(table_schema,'.',table_name) AS 'Table Name', CONCAT(ROUND(table_rows/1000000,4),'M') AS 'Number of Rows', CONCAT(ROUND(data_length/(1024*1024*1024),4),'G') AS 'Data Size', CONCAT(ROUND(index_length/(1024*1024*1024),4),'G') AS 'Index Size', CONCAT(ROUND((data_length+index_length)/(1024*1024*1024),4),'G') AS'Total'FROM information_schema.TABLES WHERE table_schema = 'example';

從圖能夠看出,1億條數據索引佔用了9.69G,數據佔用了10G,總共佔用了將近20G。索引佔用空間幾乎和數據大小同樣,所以咱們須要慎用索引,在必要的狀況下才使用,索引要小而美。

這裏寫圖片描述

相關文章
相關標籤/搜索