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

本文通过一个实例展示了MySQL在执行SQL查询时如何选择索引,以及在特定情况下优化器可能会选择错误的索引,导致查询性能下降。在示例中,当数据发生改变后,原本应使用索引'a'的查询不再选择该索引,从而影响查询速度。作者通过慢查询日志和force index来探讨这个问题,强调了索引选择对查询效率的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JIeJaitt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值