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

Same query, different execution plan with limit and sort by. Should I change the index?

我有一个 table 有 1,000,000 条记录。 Table 看起来像这样:

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`, `item_serial`),

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

所以我有两个问题:

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

2 - 您是否建议将 id 列添加到 unique_index 以获得更好的性能?我觉得添加它很奇怪,因为它在唯一性方面没有任何意义,因为 id 是自动递增的,永远不会重复,但它确实解决了这种情况下的性能问题。

如果有任何建议,我们将不胜感激。

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

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

只是 碰巧 优化器的抛硬币恰好是 22649。明天可能会有所不同 - 更大 更小。

1(for category_id)改为table中的最大值;时机可能真的很糟糕。

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

INDEX(category_id, id)

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

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

回复 0.003 秒。 -- 闻起来像是打开了查询缓存。这是骗人的。建议 re-timing 和 SELECT SQL_NO_CACHE ...

您应该更改索引吗?嗯,

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

既是索引又是唯一性约束。如果更改此设置,您将失去唯一性约束。所以,你被困在 adding

INDEX(category_id, id)