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查询:内部连接时的文件排序,限制和排序的主要内容,如果未能解决你的问题,请参考以下文章
PostgreSQL使用函数的多表关联视图在排序时的性能问题