繁体   English   中英

MySQL以DESC顺序比ASC顺序查询更快

[英]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_JOINUSE INDEX等是以下情况的拐杖:(a)您没有“正确”的索引,或者(b)优化器无法确定“正确”的事情。 也就是说,寻找其他解决方案。

在您的示例中,最好使用普通的JOININDEX(tag_id, post_id) 这将post_tag 首先进入post_tag因为有一个WHERE子句可对其进行过滤。 优化器可能会看到t.post_idp.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM