Mysql 连接查询中ORDERBY子句的优化

Mysql 连接查询中ORDERBY子句的优化,mysql,sql,database,database-design,Mysql,Sql,Database,Database Design,我需要帮助优化此查询 SELECT messages.* FROM messages INNER JOIN subscription ON subscription.entity_id = messages.entity_id WHERE subscription.user_id = 1 ORDER BY messages.timestamp DESC LIMIT 50 如果没有限制,此查询将返回200000行,运行大约需要1.3-2秒。问题似乎出在ORDE

我需要帮助优化此查询

  SELECT messages.*
   FROM messages
   INNER JOIN subscription ON subscription.entity_id = messages.entity_id
   WHERE subscription.user_id = 1
   ORDER BY messages.timestamp DESC 
   LIMIT 50
如果没有限制,此查询将返回200000行,运行大约需要1.3-2秒。问题似乎出在ORDERBY条款中。如果没有它,查询需要0.0005秒

Indexes:
    ( subscription.user_id, subscription.entity_id )
    ( subscription.entity_id )
    ( messages.timestamp )
    ( messages.entity_id, messages.timestamp )
通过将查询更改为以下内容,我能够提高性能:

SELECT messages.* FROM messages
INNER JOIN subscription ON subscription.entity_id = messages.entity_id 
INNER JOIN ( 
   SELECT message_id FROM messages ORDER BY timestamp DESC
) as temp on temp.messsage_id = messages.message_id
WHERE subscription.user_id = 1 LIMIT 50
这需要0.12秒。这是一个很好的改进,但我想知道是否可以更好。看来 如果我能以某种方式过滤第二个内部连接,那么事情会更快

谢谢

模式:

   messages 
      message_id, entity_id, message, timestamp

   subscription
      user_id, entity_id
更新

雷蒙德·尼兰德的回答解决了我最初的问题,但又出现了另一个问题

 SELECT messages.*
   FROM messages
   STRAIGHT_JOIN subscription ON subscription.entity_id = messages.entity_id
   WHERE subscription.user_id = 1
   ORDER BY messages.timestamp DESC 
   LIMIT 50
在两种情况下,直线连接效率低下:

  • 订阅表中没有用户id项

  • 消息表中几乎没有相关条目

  • 有没有关于如何解决这个问题的建议?如果不是从查询的角度来看,应用程序是什么

    更新

    解释信息

    限制50

    | id | select_type | table             | type   | possible_keys                           | key           | key_len | ref                                    | rows | Extra       |
    |  1 | SIMPLE      | messages          | index  | idx_timestamp                           | idx_timestamp | 4       | NULL                                   |   50 |             |
    |  1 | SIMPLE      | subscription      | eq_ref | PRIMARY,entity_id,user_id               | PRIMARY       | 16      | const, messages.entity_id              |    1 | Using index |
    
    无限

    | id | select_type | table             | type   | possible_keys                           | key           | key_len | ref                                    |   rows   | Extra         |
    |  1 | SIMPLE      | messages          | ALL    | entity_id_2,entity_id                   | NULL          | NULL    | NUL                                    |   255069 | Using filesort|
    |  1 | SIMPLE      | subscription      | eq_ref | PRIMARY,entity_id,user_id               | PRIMARY       | 16      | const, messages.entity_id              |        1 | Using index   |
    
    创建表语句:

    大约5000行

    subscription | CREATE TABLE `subscription` (
      `user_id`   bigint(20) unsigned NOT NULL,
      `entity_id` bigint(20) unsigned NOT NULL,
      PRIMARY KEY (`user_id`,`entity_id`),
      KEY `entity_id` (`entity_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    
    大约有255000行

    messages | CREATE TABLE `messages` (
      `message_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
      `entity_id` bigint(20) unsigned NOT NULL,
      `message` varchar(255) NOT NULL DEFAULT '',
      `timestamp` int(10) unsigned NOT NULL,
      PRIMARY KEY (`message_id`),
      KEY `entity_id` (`entity_id`,`timestamp`),
      KEY `idx_timestamp` (`timestamp`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 
    

    删除index messages.entity\u id这是多余的,请尝试直接连接。我认为mysql优化器以错误的顺序访问了您的表。 MySQL需要首先访问表消息,这样它就可以使用消息索引(实体id、时间戳),并且不需要“使用临时;使用文件排序”(如果MySQL需要创建基于MyISAM磁盘的表,并且需要使用磁盘I/O读和I/O写对其进行排序(快速排序算法),那么速度会很慢)

    我也遇到过这个问题,我像这样解决了它,但后来使用了国家/城市表

    编辑,因为在直接连接上发生了反应

    在两种情况下,直线连接效率低下:

    订阅表中没有用户id条目

    事实上,具有内部连接的MySQL优化器会触发“不可能的,在读取常量表后注意到的”,并且从不执行查询。 但是,直接连接不会触发“读取常量表后注意到的不可能”,因此需要进行(可能是完整的)索引扫描,以找到可能降低查询执行速度的用户id值。 简单的解决方法是:将现有用户id与直接连接一起使用

    消息表中几乎没有相关条目

    这里可能存在同样的问题,MySQL认为应该进行(可能是完全的)索引扫描以查找结果。但我需要看一份解释声明才能确定

    您可能还想先尝试此查询

    SELECT 
     *
    FROM (
    
     SELECT
      entity_id
    
     FROM
      subscriptions
    
     WHERE
      subscription.user_id = 1 
    )
     subscriptions
    
    INNER JOIN 
     messages
    
    ON
     subscriptions.entity_id = messages.entity_id
    
    ORDER BY
     messages.timestamp DESC
    
    LIMIT 50  
    

    您可以发布show create table语句吗?单个用户可以发布20万行?你确定吗?@DanBracuk是的,我确定你能对无用户id案例和消息案例中的几个相关条目进行解释并将结果发布在这里吗?如果你想让我们帮助优化查询,你需要向我们显示表和索引定义,以及每个表的行数。也许您的表定义不好。可能索引没有正确创建。也许你在你认为你有的专栏上没有索引。如果看不到表和索引定义,我们无法判断。我们还需要行计数,因为这会极大地影响查询优化。如果您知道如何进行
    解释
    或获取执行计划,请将结果也放在问题中。Filesort并不一定慢。这是一个误称,它并不意味着这是通过磁盘文件执行的!我知道。。。我的意思是“使用临时;使用文件排序”这一组合可能导致使用快速排序算法和大量磁盘IOTS对基于MyISAM磁盘的临时表进行排序Hanks Raymond,这绝对解决了我的问题。查询现在以.000x格式运行seconds@ypercube我不完全确定filesort不会触发基于磁盘的IO,因为离线IO_缓存tempfile,buffpek_指针,*outfile;在sql/filesort.cc中,这意味着filesort可以触发一些磁盘基址IO的写入和读取,但我还没有完全从源代码分析这一部分code@JasonM提示下次运行解释时,您可以看到MySQL尝试访问您的表的顺序。。如果在表1中使用分组依据/订单依据。MySQL需要先读取表1。如果这是错误的顺序,用一个直线连接强制它。此外,MYSQL优化器也不是基于成本的,如果表1的记录较少,则首先访问该表如果表2的记录较少,则首先访问该表。。
    
     SELECT messages.*
       FROM messages
       STRAIGHT_JOIN subscription ON subscription.entity_id = messages.entity_id
       WHERE subscription.user_id = 1
       ORDER BY messages.timestamp DESC 
       LIMIT 50
    
    SELECT 
     *
    FROM (
    
     SELECT
      entity_id
    
     FROM
      subscriptions
    
     WHERE
      subscription.user_id = 1 
    )
     subscriptions
    
    INNER JOIN 
     messages
    
    ON
     subscriptions.entity_id = messages.entity_id
    
    ORDER BY
     messages.timestamp DESC
    
    LIMIT 50