问题描述
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)