[英]MySQL query faster in DESC order than ASC order
我制作了一个简单的数据库(innodb版本5.7.9),其中包含2个表post和post_tag。
帖子具有设置为主键的单个字段ID(大整数)(约120,000个条目)。 Post_tag有2个字段,post_id(大整数)和tag_id(整数),主键位于[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中的速度较慢,因此它没有任何改变。
如果有用,我在这里上传了数据库。
在此先感谢任何可以提供帮助的人。
如果有“其他限制”,则所有下注均无效。
同时,看看你有什么...
STRAIGHT_JOIN
, USE INDEX
等是以下情况的拐杖:(a)您没有“正确”的索引,或者(b)优化器无法确定“正确”的事情。 也就是说,寻找其他解决方案。
在您的示例中,最好使用普通的JOIN
和INDEX(tag_id, post_id)
。 这将post_tag
首先进入post_tag
因为有一个WHERE
子句可对其进行过滤。 优化器可能会看到t.post_id
和p.id
相同,因此在索引中以(25, post_id)
的结尾(对于DESC
)开始,然后进行扫描。 然后检查,看看是否有一个post
进入(这是对于唯一明显的使用post
-如果再有“其他方面的限制”,所有的赌注都关闭)。
所以,回到原来的问题。 STRAIGHT_JOIN
强制先查找post
。 但是25年代在哪里? 显然接近年底 post_tag
。 因此,与从另一端开始扫描相比, ASC
需要更长的时间才能找到其中的100个(请参阅LIMIT
)!
假设这是一个多对多映射表,请执行以下操作:
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)
,请不要(tag_id, post_id DESC)
以为DESC
意味着什么-它可以识别,但可以忽略。 这两部分都将存储为ASC
。 将会发生的事情是,优化器足够聪明,可以在25秒结束时开始并向后扫描。 这里是“证明”:
US
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
MySQL通过忽略INDEX
声明中的DESC
来丢失船只的唯一情况是: ORDER BY a ASC, b DESC
无法使用INDEX(a,b)
。
大概您在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;
假定post_id
所有值都引用有效的帖子(这似乎是一个非常合理的假设),则此查询不需要JOIN
。
对于此查询, post_tag(tag_id, post_id desc)
上的索引是最佳的,MySQL对于降序排序可能会做正确的事情。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.