前面我们介绍过索引,你已经知道了在MySQL中一张表其实是可以支持多个索引的。但是,你写SQL语句的时候,并没有主动指定使用哪个索引。也就是说,使用哪个索引是由MySQL来确定的。
不知道你有没有碰到过这种情况,一条本来可以执行得很快的语句,却由于MySQL选错了索引,而导致执行速度变得很慢?
我们一起来看一个例子吧。
我们先建一个简单的表,表里有a、b两个字段,并分别建上索引:
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY(`id`),
KEY `a` (`a`),
KEY `b` (`b`)
)ENGINE=INNODB;
然后,我们往表 t 中插入10万行记录,取值按整数递增,即:(1,1,1),(2,2,2),(3,3,3)直到(100000,100000,100000)。
我是用存储过程来插入数据的,这里我贴出来方便你复现:
delimiter;;
CREATE procedure idata()
BEGIN
DECLARE i INT;
SET i=1;
WHILE (i<=100000) DO
INSERT INTO t VALUES(i,i,i);
SET i=i+1;
END WHILE;
END;;
delimiter;
CALL idata();
接下来,我们分析一条SQL语句:
mysql> select * from t where a between 10000 and 20000;
你一定会说,这个语句还用分析吗,很简单呀,a上有索引,肯定是要使用索引a的。
你说得没错,如下显示的就是使用explain命令看到的这条语句的执行情况。
mysql> explain select * from t where a between 10000 and 20000;
+----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+-----------------------+
| 1 | SIMPLE | t | NULL | range | a | a | 5 | NULL | 10001 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+-----------------------+
1 row in set, 1 warning (0.01 sec)
看上去,这条查询语句的执行也确实符合预期,key这个字段值是’a’,表示优化器选择了索引a。
不过别急,这个案例不会这么简单。在我们已经准备好的包含了10万行数据的表上,我们再做如下操作。
session A | session B |
---|---|
start transaction with consistent snapshot; | |
delete from t; call idata(); | |
explain select * from t where a between 10000 and 20000; | |
commit; |
这里,sessionA的操作你已经很熟悉了,它就是开启了一个事务。随后,sessionB把数据都删除后,又调用了idata这个存储过程,插入了10万行数据。
这时候,sessionB的查询语句select * from t where a between 10000 and 20000就不会再选择索引a了。我们可以通过慢查询日志(slow log)来查看一下具体的执行情况。
为了说明优化器选择的结果是否正确,我增加了一个对照,即:使用force index(a)来让优化器强制使用索引a(这部分内容,我还会在这篇文章的后半部分中提到)。
下面的三条SQL语句,就是这个实验过程。