Mysql查询:内部连接时的文件排序,限制和排序

Posted

技术标签:

【中文标题】Mysql查询:内部连接时的文件排序,限制和排序【英文标题】:Mysql query : file sort when inner join, limit and order by 【发布时间】:2011-08-11 03:06:43 【问题描述】:

我正在尝试优化这个查询:

SELECT articles.id 
FROM articles 
INNER JOIN articles_authors ON articles.id=articles_authors.fk_Articles 
WHERE articles_authors.fk_Authors=586 
ORDER BY articles.publicationDate LIMIT 0,50;

表格文章:

引擎:MyISAM 行格式:动态 行数:1 482 588 数据长度:788 926 672 最大数据长度:281 474 976 710 655 索引长度:127 300 608 免费数据:0 校验和:空
 创建表 `articles` (
      `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `title` VARCHAR(255) NOT NULL,
    `publicationDate` DATE NOT NULL DEFAULT '1970-01-01',
    主键(`id`),
    KEY `publicationDate` (`publicationDate`)
    ) ENGINE=MYISAM AUTO_INCREMENT=1498496 默认字符集=utf8 

表articles_authors:

引擎:MyISAM 行格式:动态 行数:1 970 750 数据长度:45 008 420 最大数据长度:281 474 976 710 655 索引长度:127 300 608 免费数据:0 校验和:空
 创建表`articles_authors` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `fk_Articles` int(10) 无符号非空,
    `fk_Authors` int(10) 无符号非空,
    主键(`id`),
    唯一键`fk_Articles_fk_Authors`(`fk_Articles`,`fk_Authors`),
    KEY `fk_Articles` (`fk_Articles`),
    KEY `fk_Authors` (`fk_Authors`),
    ) ENGINE=MyISAM AUTO_INCREMENT=2349047 默认字符集=utf8 

解释查询:

id (1), select_type(SIMPLE), TABLE(articles_authors), TYPE(ref), possible_keys(fk_Articles_fk_Authors, fk_Articles, fk_Authors), KEY (fk_Authors), Key_len(4), ref(const), ROWS(171568), extra (USING TEMPORARY; USING FILE sort)
id (1), select_type(SIMPLE), TABLE(articles), TYPE(eq_ref), possible_keys(PRIMARY), KEY (PRIMARY), Key_len(4), ref(articles_authors.fk_Authors), ROWS(1), extra ()

如您所见,SQL 查询未优化(在说明中使用文件排序)。

感谢您的帮助!

【问题讨论】:

+1,一个有据可查的问题!当人们真正包含相关信息时,我会喜欢它! 我看不出如何进一步优化,因为在 where/order 子句中你有来自两个不同表的值,你不能创建复合索引(fk_Authors,publicationDate) 编辑了答案以包含反规范化选项。 【参考方案1】:

也许这会对你有所帮助:

SELECT articles.id 
    FROM articles 
        INNER JOIN (SELECT fk_Articles FROM articles_authors WHERE articles_authors.fk_Authors=586) sub ON articles.id=sub.fk_Articles 
ORDER BY articles.publicationDate LIMIT 0,50;

【讨论】:

【参考方案2】:

正在使用索引,就像它在解释中所说的那样。

id (1), select_type(SIMPLE), TABLE(articles_authors), TYPE(ref),  
 possible_keys(fk_Articles_fk_Authors, fk_Articles, fk_Authors),`   
`KEY (fk_Authors), Key_len(4)`, ref(const), ROWS(171568),  
extra (USING TEMPORARY; USING FILE sort)

仅作为它选择的 50 行的额外,而不是按发布日期排序,它会执行文件排序。 它创建一个包含 50 个项目的临时表。然后使用 tablesort 对其进行排序。 这必须这样做,因为 mysql 不能对那些孤独的 50 个项目使用大索引,这将花费大量的 IO 访问时间。

在内存中对 50 个数字进行排序比访问磁盘上的索引更快。

您可以做一些事情来加快查询速度:

optimize table articles, articles_authors

然后重新运行查询。

编辑:通过非规范化表格文章加快建议速度

如果你像这样重写查询:

SELECT articles.id FROM articles WHERE articles.id IN (
  SELECT articles_authors.fk_articles WHERE articles_authors.fk_authors = 586 
  LIMIT 0,50
)
ORDER BY articles.publicationDate;

您可能会看到相同的性能,但它突出了问题。 如果作者 586 有 180,000 篇文章,那么 MySQL 必须在articles_authors 中从 180,000 中搜索 50 项,然后在 order 表中再次从 180,000 中搜索 50 项。

如果您合并表 article_authors 和文章,您的表文章将被非规范化 (假设一篇文章可以有多个作者) 但您不必进行连接,您可以节省第二个搜索。

CREATE TABLE `articles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `publicationDate` date NOT NULL DEFAULT '1970-01-01',
  `title` varchar(255) NOT NULL,
  `fk_Authors` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `Articles_fk_Authors` (`id`,`fk_Authors`),
KEY `fk_Authors` (`fk_Authors`),
KEY `publicationDate` (`publicationDate`)
) ENGINE=MyISAM AUTO_INCREMENT=2349047 DEFAULT CHARSET=utf8 

现在你可以像这样从中选择

SELECT articles.id FROM articles WHERE articles.Author = 586 
ORDER BY articles.publicationDate LIMIT 50,0

【讨论】:

感谢您的回复。我执行“优化表文章,articles_authors”。但性能问题仍然出现。有一个作者有 180 000 篇文章。查询耗时超过 30 秒,并注意到大 IO 访问。【参考方案3】:
SELECT articles.id 
FROM articles 
INNER JOIN articles_authors ON articles.id=articles_authors.fk_Articles 
WHERE articles.id=586 
ORDER BY articles.publicationDate LIMIT 0,50;

【讨论】:

选择文本并按下 按钮将为您格式化SQL代码。我认为每行开头的四个空格会做同样的事情 @Conrad:这就是 所做的一切......在所选文本块的每行开头放置 4 个字符。【参考方案4】:

不确定,但康拉德的建议似乎改变了排序和限制,因此您可能会按排序顺序获得随机列表的前 50 个项目,而不是排序列表的前 50 个项目。

如果按 fk_author、publicationDate 排序并有索引,是否可以查看带有连接帮助的视图?还取决于您正在优化什么,速度或磁盘空间?

在Mysql中可以使用IN吗?它可能会更好地优化吗? (示例代码,未勾选)

SELECT id FROM articles WHERE id IN 
(SELECT fk_Articles FROM articles_authors WHERE fk_Authors=586) as IDs
ORDER BY publicationDate LIMIT 0,50;

【讨论】:

【参考方案5】:

这实际上可能有效,具体取决于您的数据。

SELECT articles.id 
FROM articles 
INNER JOIN articles_authors ON articles.id=articles_authors.fk_Articles 
WHERE articles_authors.fk_Authors=586 
ORDER BY articles.publicationDate LIMIT 0,50;

如果articles_authors.fk_Authors=586 根据您的数据库引擎收集的统计数据导致相当少的行,那么获取全部并获取前50行会更便宜。

相比之下,如果它指向大多数文章,则查询articles.publicationDate 上的索引并过滤掉无效行直到获得请求的50 行会更便宜。

【讨论】:

以上是关于Mysql查询:内部连接时的文件排序,限制和排序的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 嵌套连接排序

MySQL避免内部查询中的文件排序

MySQL使用笔记排序和限制数据记录查询

PostgreSQL使用函数的多表关联视图在排序时的性能问题

mysql常用基础操作语法--对数据排序和限制结果数量的条件查询命令行模式

使用内连接进行查询的方法,用于更新 X 跳过锁定、排序和限制