当前位置:网站首页>MySQL 为什么有时候会选错索引?

MySQL 为什么有时候会选错索引?

2022-07-28 09:34:00 JIeJaitt

前面我们介绍过索引,你已经知道了在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 Asession 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语句,就是这个实验过程。

原网站

版权声明
本文为[JIeJaitt]所创,转载请带上原文链接,感谢
https://jiejaitt.blog.csdn.net/article/details/125965795