相同的查询,具有限制和排序依据的不同执行计划。我应该更改索引吗?
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)
我有一个 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)