MySQL何时使用索引探究

摘要:MySQL何时使用索引,跟着笔者通过实操来验证下。

大家已经知道,索引的核心价值是快速找到特定数据,那MySQL具体在哪些操作中会用到索引呢?我们一起来看看。

  • 查找与WHERE条件匹配的行时
root@database-one 14:31:  [gftest]> select count(*) from emp_copy;
+----------+
| count(*) |
+----------+
|    20480 |
+----------+
1 row in set (0.01 sec)

root@database-one 14:32:  [gftest]> show index from emp_copy \G
*************************** 1. row ***************************
        Table: emp_copy
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: eno
    Collation: A
  Cardinality: 20864
     Sub_part: NULL
       Packed: NULL
         Null: 
   Index_type: BTREE
      Comment: 
Index_comment: 
*************************** 2. row ***************************
        Table: emp_copy
   Non_unique: 1
     Key_name: idx_empcopy_ename
 Seq_in_index: 1
  Column_name: ename
    Collation: A
  Cardinality: 5
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
Index_comment: 
2 rows in set (0.01 sec)

root@database-one 14:32:  [gftest]> explain select * from emp_copy where ename='刘杰';
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-------+
| id | select_type | table    | partitions | type | possible_keys     | key               | key_len | ref   | rows | filtered | Extra |
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | emp_copy | NULL       | ref  | idx_empcopy_ename | idx_empcopy_ename | 33      | const | 4096 |   100.00 | NULL  |
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

 

emp_copy表有2万多行数据,eno、ename列上有索引,当查找“刘杰”的信息时,MySQL就通过ename列上的索引去快速匹配。

  • 排除某些行时
root@database-one 14:35:  [gftest]> explain select * from emp_copy where ename='刘杰' and sal>8000;
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys     | key               | key_len | ref   | rows | filtered | Extra       |
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | emp_copy | NULL       | ref  | idx_empcopy_ename | idx_empcopy_ename | 33      | const | 4096 |    33.33 | Using where |
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

root@database-one 14:35:  [gftest]> explain select * from emp_copy where ename<>'刘杰' and sal>8000;
+----+-------------+----------+------------+------+-------------------+------+---------+------+-------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys     | key  | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+----------+------------+------+-------------------+------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | emp_copy | NULL       | ALL  | idx_empcopy_ename | NULL | NULL    | NULL | 20864 |    16.67 | Using where |
+----+-------------+----------+------------+------+-------------------+------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

root@database-one 14:36:  [gftest]> explain select * from emp_copy where eno<>12  and sal>8000;
+----+-------------+----------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+
| id | select_type | table    | partitions | type  | possible_keys | key     | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | emp_copy | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL | 10441 |    33.33 | Using where |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

root@database-one 14:37:  [gftest]> explain select * from emp_copy where eno<>12 and ename<>'刘杰' and sal>8000;
+----+-------------+----------+------------+-------+---------------------------+---------+---------+------+-------+----------+-------------+
| id | select_type | table    | partitions | type  | possible_keys             | key     | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+----------+------------+-------+---------------------------+---------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | emp_copy | NULL       | range | PRIMARY,idx_empcopy_ename | PRIMARY | 4       | NULL | 10441 |    16.67 | Using where |
+----+-------------+----------+------------+-------+---------------------------+---------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

root@database-one 14:39:  [gftest]> explain select * from emp_copy where eno<>12 and ename='刘杰' and sal>8000;
+----+-------------+----------+------------+------+---------------------------+-------------------+---------+-------+------+----------+------------------------------------+
| id | select_type | table    | partitions | type | possible_keys             | key               | key_len | ref   | rows | filtered | Extra                              |
+----+-------------+----------+------------+------+---------------------------+-------------------+---------+-------+------+----------+------------------------------------+
|  1 | SIMPLE      | emp_copy | NULL       | ref  | PRIMARY,idx_empcopy_ename | idx_empcopy_ename | 33      | const | 4172 |    16.68 | Using index condition; Using where |
+----+-------------+----------+------------+------+---------------------------+-------------------+---------+-------+------+----------+------------------------------------+
1 row in set, 1 warning (0.00 sec)

 

如果可以使用的索引有多个,MySQL通常使用查找行数最小的索引。

  • 针对多列索引,即组合索引,使用最左边的前缀列查询时均会使用索引

例如,在表上有基于(col1、col2、col3)三列的索引,则以(col1)、(col1,col2)和(col1,col2,col3)作为条件搜索时都能使用上索引。

root@database-one 14:54:  [gftest]> create index idx_empcopy_3col on emp_copy(age,sal,hiredate);
Query OK, 0 rows affected (0.09 sec)
Records: 0  Duplicates: 0  Warnings: 0

root@database-one 14:56:  [gftest]> explain select * from emp_copy where age>25;
+----+-------------+----------+------------+------+------------------+------+---------+------+-------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys    | key  | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+----------+------------+------+------------------+------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | emp_copy | NULL       | ALL  | idx_empcopy_3col | NULL | NULL    | NULL | 20864 |    50.00 | Using where |
+----+-------------+----------+------------+------+------------------+------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

root@database-one 14:56:  [gftest]> explain select * from emp_copy where age>25 and sal>8000;
+----+-------------+----------+------------+------+------------------+------+---------+------+-------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys    | key  | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+----------+------------+------+------------------+------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | emp_copy | NULL       | ALL  | idx_empcopy_3col | NULL | NULL    | NULL | 20864 |    16.66 | Using where |
+----+-------------+----------+------------+------+------------------+------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

root@database-one 14:56:  [gftest]> explain select * from emp_copy where age>25 and sal>8000 and hiredate>date_add(now(), interval 1 year);
+----+-------------+----------+------------+------+------------------+------+---------+------+-------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys    | key  | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+----------+------------+------+------------------+------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | emp_copy | NULL       | ALL  | idx_empcopy_3col | NULL | NULL    | NULL | 20864 |     5.55 | Using where |
+----+-------------+----------+------------+------+------------------+------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

 

如果where条件中不包含多列索引最左边的列,则无法使用索引。

root@database-one 15:00:  [gftest]> explain select * from emp_copy where sal>8000;
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | emp_copy | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 20864 |    33.33 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

root@database-one 15:01:  [gftest]> explain select * from emp_copy where hiredate>date_add(now(), interval 1 year);
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | emp_copy | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 20864 |    33.33 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

root@database-one 15:01:  [gftest]> explain select * from emp_copy where sal>8000 and hiredate>date_add(now(), interval 1 year);
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | emp_copy | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 20864 |    11.11 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

 

  • 关联其它表时
root@database-one 15:17:  [gftest]> create table dept(deptno int(2) primary key,deptname varchar(20),adress varchar(30)) engine=innodb default charset=utf8;
Query OK, 0 rows affected (0.01 sec)

root@database-one 15:17:  [gftest]> insert into dept values(10,'财务','北京'),(20,'客服','西安'),(30,'技术','深圳');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

root@database-one 15:17:  [gftest]> explain select * from emp_copy e join dept d on e.deptno=d.deptno where sal>8000;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra                                              |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+
|  1 | SIMPLE      | d     | NULL       | ALL  | PRIMARY       | NULL | NULL    | NULL |     3 |   100.00 | NULL                                               |
|  1 | SIMPLE      | e     | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 20864 |     3.33 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

root@database-one 15:18:  [gftest]> create index idx_empcopy_deptno on emp_copy(deptno);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

root@database-one 15:18:  [gftest]> explain select * from emp_copy e join dept d on e.deptno=d.deptno where sal>8000;
+----+-------------+-------+------------+------+--------------------+--------------------+---------+-----------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys      | key                | key_len | ref             | rows | filtered | Extra       |
+----+-------------+-------+------------+------+--------------------+--------------------+---------+-----------------+------+----------+-------------+
|  1 | SIMPLE      | d     | NULL       | ALL  | PRIMARY            | NULL               | NULL    | NULL            |    3 |   100.00 | NULL        |
|  1 | SIMPLE      | e     | NULL       | ref  | idx_empcopy_deptno | idx_empcopy_deptno | 5       | gftest.d.deptno | 6954 |    33.33 | Using where |
+----+-------------+-------+------------+------+--------------------+--------------------+---------+-----------------+------+----------+-------------+
2 rows in set, 1 warning (0.01 sec)

 

  • group by或order by时
root@database-one 15:38:  [gftest]> explain select deptno,sum(sal),avg(sal) from emp_copy group by deptno;
+----+-------------+----------+------------+-------+--------------------+--------------------+---------+------+-------+----------+-------+
| id | select_type | table    | partitions | type  | possible_keys      | key                | key_len | ref  | rows  | filtered | Extra |
+----+-------------+----------+------------+-------+--------------------+--------------------+---------+------+-------+----------+-------+
|  1 | SIMPLE      | emp_copy | NULL       | index | idx_empcopy_deptno | idx_empcopy_deptno | 5       | NULL | 20864 |   100.00 | NULL  |
+----+-------------+----------+------------+-------+--------------------+--------------------+---------+------+-------+----------+-------+
1 row in set, 1 warning (0.00 sec)

root@database-one 15:43:  [gftest]> explain select * from emp_copy order by age,sal,hiredate;
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+----------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra          |
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+----------------+
|  1 | SIMPLE      | emp_copy | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 20864 |   100.00 | Using filesort |
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+----------------+
1 row in set, 1 warning (0.00 sec)

root@database-one 15:45:  [gftest]> explain select age,sal,hiredate from emp_copy order by age,sal,hiredate;
+----+-------------+----------+------------+-------+---------------+------------------+---------+------+-------+----------+-------------+
| id | select_type | table    | partitions | type  | possible_keys | key              | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+----------+------------+-------+---------------+------------------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | emp_copy | NULL       | index | NULL          | idx_empcopy_3col | 15      | NULL | 20864 |   100.00 | Using index |
+----+-------------+----------+------------+-------+---------------+------------------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

root@database-one 15:47:  [gftest]> explain select ename,age,sal,hiredate from emp_copy order by age,sal,hiredate;
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+----------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra          |
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+----------------+
|  1 | SIMPLE      | emp_copy | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 20864 |   100.00 | Using filesort |
+----+-------------+----------+------------+------+---------------+------+---------+------+-------+----------+----------------+
1 row in set, 1 warning (0.00 sec)

 

可以看到group by或order by时,MySQL并不是列上有索引就必定使用,还跟select要返回的列有关系。当select要返回的列都包含在索引中时,MySQL会优先使用索引,这样可以不必回表,获得更好的效能。

最后,还要注意,索引对小表价值不大,因为直接读取整个表可能比通过索引检索更快。索引对要读取表中大部分甚至全部数据的SQL价值不大,因为当SQL要访问表中大部分行时,按顺序读取比通过索引读取更快,顺序读取可以使磁盘查找最小化。

© 2020, morinson. 版权所有. 欢迎转载,但请保留作者及出处。