关于索引

一.测试数据

MyISAM:

CREATE TABLE `test` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `uid` int(11) NOT NULL,
  `name` char(255) CHARACTER SET gbk NOT NULL,
  `time` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `uid` (`uid`) USING BTREE,
  KEY `name_time` (`name`,`time`)
) ENGINE=MyISAM;

  

INSERT INTO test VALUES ('1', '14', 'test', '1513338971');
INSERT INTO test VALUES ('2', '15', 'jack', '1513338980');
INSERT INTO test VALUES ('3', '16', 'home2', '1513338980');

  

InnoDB:

CREATE TABLE `test2` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `uid` int(11) NOT NULL,
  `name` char(255) CHARACTER SET gbk NOT NULL,
  `time` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `uid` (`uid`) USING BTREE,
  KEY `name_time` (`name`,`time`)
) ENGINE=InnoDB

  

INSERT INTO test2 VALUES ('1', '14', 'test', '1513338971');
INSERT INTO test2 VALUES ('2', '15', 'jack', '1513338980');
INSERT INTO test2 VALUES ('3', '16', 'home2', '1513338980');

  

 

二.实例分析,只有where的情形下

 1.为什么要有最左前缀限制?

对于多列索引,最左前缀是必须的.否者mysql无法使用索引,因为符合索引是按第一列,第二列,第三列...一次来排序的.不符合最左前缀原则,mysql将放弃使用索引.

如:

mysql> explain select * from test where time=1513338971;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

 

mysql> explain select * from test2 where time=1513338971;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | test2 | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

  

2.AND

对于where中的and,只要and两边有一边的字段有索引,就能用到索引.

mysql> explain select * from test where time=1513338971 and name='jack';
+----+-------------+-------+------+---------------+-----------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key       | key_len | ref         | rows | Extra       |
+----+-------------+-------+------+---------------+-----------+---------+-------------+------+-------------+
|  1 | SIMPLE      | test  | ref  | name_time     | name_time | 515     | const,const |    1 | Using where |
+----+-------------+-------+------+---------------+-----------+---------+-------------+------+-------------+
1 row in set (0.00 sec)

mysql> explain select * from test2 where time=1513338971 and name='jack';
+----+-------------+-------+------+---------------+-----------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key       | key_len | ref         | rows | Extra       |
+----+-------------+-------+------+---------------+-----------+---------+-------------+------+-------------+
|  1 | SIMPLE      | test2 | ref  | name_time     | name_time | 515     | const,const |    1 | Using where |
+----+-------------+-------+------+---------------+-----------+---------+-------------+------+-------------+
1 row in set (0.00 sec)

  

3.OR

对于OR,只要有一个字段没有索引,就不会用到索引

mysql> explain select * from test where  name='jack' or time=1513338971;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | test  | ALL  | name_time     | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

mysql> explain select * from test2 where  name='jack' or time=1513338971;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | test2 | ALL  | name_time     | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

  

 

三.Order(不带where条件)

(一).单列索引

1.除非强制走索引或覆盖索引,否者myisam不会用到索引,而进行额外排序(Extra列中出现using filesort)

mysql> explain select * from test order by id desc;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+

myisam是非聚集索引,优化器认为排序完后还需要具体的去读取数据行,所以干脆放弃了使用索引,也就无法索引扫描排序.除非是覆盖索引,否则优化器都不会用到索引,而进行额外排序.

 

2.InnoDB则可以用到索引,只扫描索引就可以排序(type列出现index,表明使用了索引排序)

mysql> explain select * from test2 order by id desc;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------+
|  1 | SIMPLE      | test2 | index | NULL          | PRIMARY | 4       | NULL |    3 |       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------+

因为是聚集索引,索引和数据是在一起的,优化器认为不必再去查找数据.所以会用到主键索引. 

 

(二).复合索引 

1.覆盖索引情况下,只要排序字段中排序方向一致,两者均可用到索引扫描排序.反向不一致,就不会用到索引排序.

mysql> explain select name,time from test order by name asc,time asc;
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key       | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------------+
|  1 | SIMPLE      | test  | index | NULL          | name_time | 515     | NULL |    3 | Using index |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------------+

  

mysql> explain select name,time from test2 order by name asc,time asc;
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key       | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------------+
|  1 | SIMPLE      | test2 | index | NULL          | name_time | 515     | NULL |    3 | Using index |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------------+

  

2.非覆盖索引情况下,即便方向一致,两者均需要去进行额外排序,而且优化器对两者均不进行任何索引优化

mysql> explain select * from test order by name asc,time asc;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+

  

mysql> explain select * from test2 order by name asc,time asc;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | test2 | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+

  

可以强制让其走索引

mysql> explain select * from test force index(name_time) order by name asc,time asc;
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------+
| id | select_type | table | type  | possible_keys | key       | key_len | ref  | rows | Extra |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------+
|  1 | SIMPLE      | test  | index | NULL          | name_time | 515     | NULL |    3 |       |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------+

mysql> explain select * from test2 force index(name_time) order by name asc,time asc;
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------+
| id | select_type | table | type  | possible_keys | key       | key_len | ref  | rows | Extra |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------+
|  1 | SIMPLE      | test2 | index | NULL          | name_time | 515     | NULL |    3 |       |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------+

  

方向不一致就不用去说了.无论如何都会进行一次额外排序的.

 

四.Where+Order

(一).单列索引

1.只要where中有单列索引,一定会用到索引的.

mysql> explain select * from test where uid=14 order by id desc;
+----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra                       |
+----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------------+
|  1 | SIMPLE      | test  | ref  | uid           | uid  | 4       | const |    1 | Using where; Using filesort |
+----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------------+
1 row in set (0.00 sec)

mysql> explain select * from test2 where uid=14 order by id desc;
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+
|  1 | SIMPLE      | test2 | ref  | uid           | uid  | 4       | const |    1 | Using where |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+
1 row in set (0.00 sec)

  

但是不同之处在于,myisam的非聚集索引,在排序时还需要数据行取数据,所以会用到额外排序.而innodb因为聚集索引,所以不会进行额外排序.(索引肯定是排好序的)

2.where中没有索引,order中有索引,也不会走索引

mysql> explain select * from test where time=14 order by uid desc;
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                       |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
1 row in set (0.00 sec)

mysql> explain select * from test2 where time=14 order by uid desc;
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                       |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | test2 | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
1 row in set (0.00 sec)

 

where中没有索引,order中有索引,也不会走索引 

(二).复合索引

仅讨论where列和order列构建的复合索引

mysql> explain select * from test where name='jack' order by time asc;
+----+-------------+-------+------+---------------+-----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key       | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+-----------+---------+-------+------+-------------+
|  1 | SIMPLE      | test  | ref  | name_time     | name_time | 510     | const |    1 | Using where |
+----+-------------+-------+------+---------------+-----------+---------+-------+------+-------------+
1 row in set (0.00 sec)

mysql> explain select * from test2 where name='jack' order by time asc;
+----+-------------+-------+------+---------------+-----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key       | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+-----------+---------+-------+------+-------------+
|  1 | SIMPLE      | test2 | ref  | name_time     | name_time | 510     | const |    1 | Using where |
+----+-------------+-------+------+---------------+-----------+---------+-------+------+-------------+
1 row in set (0.00 sec)

  

只有where列是一个常量,才使用索引扫描.否则将是额外排序.

mysql> explain select * from test where name>'jack' order by time asc;
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-----------------------------+
| id | select_type | table | type  | possible_keys | key       | key_len | ref  | rows | Extra                       |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | test  | range | name_time     | name_time | 510     | NULL |    2 | Using where; Using filesort |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-----------------------------+
1 row in set (0.00 sec)

mysql> explain select * from test2 where name>'jack' order by time asc;
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-----------------------------+
| id | select_type | table | type  | possible_keys | key       | key_len | ref  | rows | Extra                       |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | test2 | range | name_time     | name_time | 510     | NULL |    1 | Using where; Using filesort |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-----------------------------+
1 row in set (0.00 sec)

  

 

mysql每次只能使用一个索引,无论是单例索引还是符合索引.

只有用到索引,才有可能用到索引扫描(一句废话)

 

 

 

posted @ 2017-12-18 11:35  rorshach  阅读(171)  评论(0编辑  收藏  举报