【问题标题】:Mysql optimize slow query with explainMysql用解释优化慢查询
【发布时间】:2013-10-11 09:11:28
【问题描述】:

我正在开发 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 子句

以下是查询 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:

解释查询 2:

解释查询 3:

解释查询 4:

查询 1 的分析器结果:

查询 2 的分析器结果:

查询 3 的探查器结果:

查询 4 ​​的分析器结果:

以下是表格定义:

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 和 score 排序结果?

【问题讨论】:

  • 查询 3 和查询 4 ​​执行需要多长时间?您只提到了查询 1 和 2。
  • 为什么查询 2 将连接表倒置?查询 2 的速度要快得多,因为它所做的第一件事是在执行任何连接之前使用 where 子句过滤分数,而查询 1 必须尝试连接所有(未过滤的)项目..
  • @GarethD 抱歉,忘记添加了。它们和查询 2 一样快
  • @StevieG 根据我链接的 Stack Overflow 问题中指出的 MySQL 文档,ORDER BY 列必须是连接中的第一个表之一,所以当我按 score.score 排序时,我输入了 score表作为连接中的第一个,当我按 item.zen_time 排序时,我将项目作为连接中的第一个。
  • 那么如果你运行查询 2 并将 order by 更改为 ORDER BY zen_time DESC,你的性能会更差吗?

标签: mysql sql database performance explain


【解决方案1】:

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

我需要的结果集是四个:

  1. 从特定提要中选择所有商品,按 SCORE.score 排序(智能排序)
  2. 从特定提要中选择所有项目,按 ITEM.zen_time 排序(时间顺序)
  3. 选择所有项目,按 SCORE.score 排序(智能排序)
  4. 选择所有项目,按ITEM.zen_time(时间顺序)排序

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

  • STRAIGHT_JOIN 是/否
  • 第一个 JOIN 表分数/项目
  • 特定 Feed 的 WHERE 条件是/否
  • 按分数/zen_time 排序

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

结果如下:

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

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

【讨论】:

    猜你喜欢
    • 2012-03-02
    • 1970-01-01
    • 2021-08-10
    • 1970-01-01
    • 2016-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    相关资源
    最近更新 更多