MySQL在DESC命令中的查询速度比ASC命令快

时间:2016-01-31 15:18:22

标签: mysql performance sorting innodb

我制作了一个简单的数据库(innodb版本5.7.9),包含2个表,post和post_tag。

Post将单个字段ID(big int)设置为主键(大约120,000个条目)。 Post_tag有2个字段,post_id(big int)和tag_id(int),主键位于[post_id,tag_id]。

以下查询在~1ms内运行:

SELECT 
   SQL_NO_CACHE p.id 
FROM 
   post as p 
STRAIGHT_JOIN 
   post_tag t
WHERE  
   t.post_id = p.id AND t.tag_id = 25 
ORDER BY 
   p.id DESC
LIMIT 0, 100

但如果我将ORDER BY更改为ASC,它的运行速度会慢大约100倍!而那种我感兴趣的那种......

知道为什么吗?

最初,我希望ID排序DESC,我注意到它比ASC慢。我读到索引的自然排序是ASC,所以我恢复了所有ID(通过做ID = SOMETHING BIG - ID),但是它没有改变任何东西,因为它在ASC中现在变慢了。

我上传了数据库here以防它有用。

非常感谢能够提供帮助的任何人。

以下是解释: enter image description here

2 个答案:

答案 0 :(得分:1)

如果有"其他约束",则所有投注均已关闭。

同时,看看你有什么......

STRAIGHT_JOINUSE INDEX等等,是因为(a)你没有“权利”这样的权利。索引,或(b)优化器无法弄清楚'权利'要做的事。 也就是说,寻找其他解决方案。

在您的示例中,使用普通JOININDEX(tag_id, post_id)会更好。这将允许它首先转到post_tag ,因为有一个WHERE子句让它在那里过滤。优化程序可能会看到t.post_idp.id相同,因此请在索引中开始DESC的{​​{1}}}结束,然后扫描。然后它检查是否有(25, post_id)条目(这是post唯一明显的用途 - 再次,如果有"其他约束",所有投注都关闭)。

所以,回到最初的问题。 post 强制首先查看STRAIGHT_JOIN。但25岁的人在哪里?显然位于post end 附近。因此,post_tag需要更长时间才能找到100(参见ASC),而不是扫描从另一端开始!

假设这是一个多对多映射表,请执行以下操作:

LIMIT

我在my blog中讨论了很多原因。

如果按照建议添加CREATE TABLE post_tag ( post_id ..., tag_id ..., PRIMARY KEY(post_id, tag_id), INDEX (tag_id, post_id) ) ENGINE=InnoDB; ,请不要嘲笑(tag_id, post_id DESC)意味着什么 - 它会被识别,但会被忽略。 两个部分都将存储DESC。会发生什么是优化器足够聪明,可以在25秒结束时开始向后扫描。这里"证明":

ASCUS

INDEX(state, population)

mysql> FLUSH STATUS; mysql> SELECT city, population FROM US WHERE state = 'OH' ORDER BY population DESC LIMIT 5; +------------+------------+ | city | population | +------------+------------+ | Columbus | 736836 | | Cleveland | 449514 | | Toledo | 306974 | | Cincinnati | 306382 | | Akron | 208414 | +------------+------------+ mysql> SHOW SESSION STATUS LIKE 'Handler%'; | Handler_read_key | 1 | -- get started at end of Ohio | Handler_read_prev | 4 | -- read (5-1) more, scanning backwards 声明中忽略DESC的MySQL错过了唯一的情况是:INDEX无法使用ORDER BY a ASC, b DESC

答案 1 :(得分:0)

据推测,您在post(id)上有一个索引(例如,这是为主键自动创建的)。当使用ORDER BY的索引时,MySQL有时会关注索引的顺序。

通过更改顺序,您将更改查询计划,以便进行必要的排序。

我建议只使用一个表来编写查询:

SELECT t.post_id 
FROM post_tag t
WHERE t.tag_id = 25 
ORDER BY t.post_id DESC
LIMIT 0, 100;

此查询不需要JOIN,假设post_id的所有值都引用有效帖子(这似乎是一个非常合理的假设)。

对于此查询,post_tag(tag_id, post_id desc)上的索引是最佳的,而MySQL可能会为降序排序做正确的事。