相同的查询,具有限制和排序依据的不同执行计划我应该更改索引吗?

问题描述

我有一个包含1,000,000条记录的表。表格如下:

CREATE TABLE `items` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,`category_id` int(10) unsigned NOT NULL,`item_serial` varchar(255) NOT NULL,PRIMARY KEY (`id`),UNIQUE KEY `unique_index` (`category_id`,`item_serial`),)

当我执行此查询时:

select * from `items`
where `category_id` = 1  
order by `id` asc 
limit 22649;

大约需要0.5秒,我发现很多。

如果执行查询的解释,则会得到以下结果:

type | possible_keys  | key            | key_len | ref   | rows  | filtered | Extra
-----|----------------|----------------|---------|-------|-------|----------|-----------------------------------------
ref  | unique_index   | unique_index   | 4       | const | 498253| 100.00   | Using where; Using index; Using filesort

如果我执行完全相同的查询,但只从限制中删除1位数字,那么22648而不是22649:

select * from `items`
where `category_id` = 1  
order by `id` asc 
limit 22648;

查询持续时间要快得多,大约为0.003秒。

我得到以下结果进行解释:

type | possible_keys  | key            | key_len | ref   | rows  | filtered | Extra
-----|----------------|----------------|---------|-------|-------|----------|-------------
ref  | unique_index   | PRIMARY        | 8       | null  |  45296| 50.00    | Using where

如您所见,两种查询的解释都不同,查询持续时间也大不相同。

我也玩过unique_index,并将id列添加到该索引中,将其更改为:

UNIQUE KEY `unique_index` (`id`,`category_id`,

这样做的时候,我再也没有得到不同的结果,但是使用新索引总是得到相同的解释结果,并且快速结果大约为0.003秒。

所以我有2个问题:

1-为什么具有不同限制的同一查询会产生不同的执行计划?

2-您是否建议将id列添加到unique_index以提高性能?我发现添加它很奇怪,因为就ID而言,它是唯一的,因为ID是自动递增的,并且永远不会重复,但是在这种情况下,它确实解决了此性能问题。

对此表示感谢。

解决方法

您发现了优化器之间的撕裂情况

  • category进行过滤-覆盖索引,但需要排序
  • 按顺序读取数据-避免按id顺序进行排序,但必须检查每一行。

发生了 优化器的硬币翻转恰好在22649的情况​​。明天可能有所不同-较大的较小。

1(对于category_id)更改为表中的最大值;时间可能真的很糟糕。

如前所述,具有这两列的索引开始是最佳的:

INDEX(category_id,id)

注意:InnoDB将PRIMARY KEY列添加到每个二级索引的末尾。实际上,该表是两个BTree:

(id,category_id,item_serial) -- All the data,in PK order
(category_id,item_serial,id) -- secondary index plus PK

Re 0.003秒-闻起来像查询缓存已打开。这是欺骗性的。建议使用SELECT SQL_NO_CACHE ...

重新定时

您应该更改索引吗?好吧,

UNIQUE KEY `unique_index` (`category_id`,`item_serial`)

既是索引又是唯一性约束。如果更改此设置,则将失去唯一性约束。因此,您受困于添加

INDEX(category_id,id)