Mysql优化慢查询与解释

时间:2013-10-04 11:26:07

标签: mysql sql database performance explain

我正在研究MySQL 5.5.29-0ubuntu0.12.04.1。

我需要创建一个可以按日期和分数对结果进行排序的查询。

我在stackoverflow(特别是this)上阅读了有关如何优化查询的文档和帖子,但我仍然在努力做好。 关键的发现是,为了避免使用临时表,ORDER BY或GROUP BY必须只包含连接队列中第一个表的列,这就是为什么使用STRAIGHT_JOIN子句和两个略有不同的查询。

为避免混淆,我将为各种查询配置分配一个数字:

  1. 按日期订购STRAIGHT_JOIN条款
  2. 使用STRAIGHT_JOIN条款按分数排序
  3. 按日期排序,没有STRAIGHT_JOIN条款
  4. 没有STRAIGHT_JOIN条款的分数排序
  5. 以下是查询1,大约需要2.5秒才能完成:

    SELECT STRAIGHT_JOIN item.id AS id
    FROM item 
    INNER JOIN score ON item.id = score.item_id 
    LEFT JOIN url ON item.url_id = url.id 
    LEFT JOIN doc ON url.doc_id = doc.id 
    INNER JOIN feed ON feed.id = item.feed_id 
    INNER JOIN user_feed ON feed.id = user_feed.feed_id AND score.user_id = user_feed.user_id 
    LEFT JOIN star ON item.id = star.item_id AND score.user_id = star.user_id 
    JOIN unseen ON item.id = unseen.item_id AND score.user_id = unseen.user_id 
    WHERE score.user_id = 1 AND user_feed.id = 7 
    ORDER BY zen_time DESC 
    LIMIT 0, 10
    

    以下是查询2(第一个连接表被反转,排序列不同),只需0.01秒即可完成:

    SELECT STRAIGHT_JOIN item.id AS id
    FROM score
    INNER JOIN item ON item.id = score.item_id 
    LEFT JOIN url ON item.url_id = url.id 
    LEFT JOIN doc ON url.doc_id = doc.id 
    INNER JOIN feed ON feed.id = item.feed_id 
    INNER JOIN user_feed ON feed.id = user_feed.feed_id AND score.user_id = user_feed.user_id 
    LEFT JOIN star ON item.id = star.item_id AND score.user_id = star.user_id 
    JOIN unseen ON item.id = unseen.item_id AND score.user_id = unseen.user_id 
    WHERE score.user_id = 1 AND user_feed.id = 7 
    ORDER BY score DESC 
    LIMIT 0, 10
    

    以下是查询的EXPLAIN结果。

    解释查询1: enter image description here

    解释查询2: enter image description here

    解释查询3: enter image description here

    解释查询4: enter image description here

    查询1的Profiler结果: enter image description here

    查询2的Profiler结果: enter image description here

    查询3的Profiler结果: enter image description here

    查询4的Profiler结果: enter image description here

    以下是表格定义:

    CREATE TABLE `doc` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `md5` char(32) DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `Md5_index` (`md5`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    CREATE TABLE `feed` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `url` text NOT NULL,
    `title` text,
    PRIMARY KEY (`id`),
    FULLTEXT KEY `Title_url_index` (`title`,`url`)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
    
    CREATE TABLE `item` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `feed_id` bigint(20) unsigned NOT NULL,
    `url_id` bigint(20) unsigned DEFAULT NULL,
    `md5` char(32) NOT NULL,
    PRIMARY KEY (`id`),
    KEY `Md5_index` (`md5`),
    KEY `Zen_time_index` (`zen_time`),
    KEY `Feed_index` (`feed_id`),
    KEY `Url_index` (`url_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    CREATE TABLE `score` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `user_id` bigint(20) unsigned NOT NULL,
    `item_id` bigint(20) unsigned NOT NULL,
    `score` float DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `User_item_index` (`user_id`,`item_id`),
    KEY Score_index (`score`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    CREATE TABLE `star` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `user_id` bigint(20) unsigned NOT NULL,
    `item_id` bigint(20) unsigned NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `User_item_index` (`user_id`,`item_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    CREATE TABLE `unseen` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `user_id` bigint(20) unsigned NOT NULL,
    `item_id` bigint(20) unsigned NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `User_item_index` (`user_id`,`item_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    CREATE TABLE `url` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `doc_id` bigint(20) unsigned DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY Doc_index (`doc_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    CREATE TABLE `user` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `email` varchar(255) NOT NULL,
    PRIMARY KEY (`id`),
    KEY `IDX_Email` (`email`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    CREATE TABLE `user_feed` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `user_id` bigint(20) unsigned NOT NULL,
    `feed_id` bigint(20) unsigned NOT NULL,
    PRIMARY KEY (`id`),
    KEY `User_feed_index` (`user_id`,`feed_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    以下是查询中涉及的表的行数:

    Score: 68657
    Item: 197602
    Url: 198354
    Doc: 186113
    Feed: 754
    User_feed: 721
    Star: 0
    Unseen: 150762
    

    我应该采取哪种方法,因为我的程序需要能够以最快的方式通过zen_time和得分来订购结果?

1 个答案:

答案 0 :(得分:0)

由于查询速度不同,我决定根据我想要实现的各种结果进行更准确的分析。

我需要的结果集是四个:

  1. 选择特定Feed中的所有项目,按SCORE.score(智能订单)
  2. 订购
  3. 选择特定Feed中的所有项目,按ITEM.zen_time(时间顺序)
  4. 订购
  5. 选择所有项目,按SCORE.score(智能订单)
  6. 订购
  7. 选择所有项目,按ITEM.zen_time(时间顺序)
  8. 订购

    查询必须适应这些条件,其可变部分是:

    • STRAIGHT_JOIN是/否
    • First JOIN table score / item
    • 特定Feed上的条件是/否
    • ORDER BY score / zen_time

    所有测试都已使用SELECT SQL_NO_CACHE指令执行。

    以下是结果: enter image description here

    现在很清楚我必须做什么:

    1. 没有STRAIGHT_JOIN,第一个JOIN表SCORE
    2. 没有STRAIGHT_JOIN,第一个JOIN表SCORE
    3. STRAIGHT_JOIN(我在这里击败了MySQL引擎:D),第一个JOIN表SCORE
    4. STRAIGHT_JOIN(我在这里击败了MySQL引擎:D),第一个JOIN表ITEM
相关问题