【问题标题】:Increasing query performance on joins and using order by提高连接的查询性能并使用 order by
【发布时间】:2011-02-20 18:11:35
【问题描述】:

我正在为一个项目使用 Grails 框架,该项目使用 Hibernate for ORM。 Grails 支持命名查询,它转换为休眠条件。我一直在使用 P6Spy 和 SQLProfiler 来尝试提高这些命名查询之一的性能。

问题是当我运行没有 order by 子句的查询时,它会在大约 0.1200 秒内运行。当我添加 order by 子句时,它增加到 15 秒。

这是(大致)最终结果查询的样子:

Select 
    * 
from 
    article
left outer join 
    feed_articles 
on 
    article.id = feed_articles.article_id
where 
    ( 
        feed_articles.feed_id = 1 or 
        feed_articles.feed_id = 43 or 
        feed_articles.feed_id = 67
    )
order by 
    article.updated
limit
    50

如果有任何 grails 专家阅读,这里是文章域的精简版本:

static hasMany = [articleFeeds: ArticleFeed]

Date updated

static namedQueries = {

    containedInFeeds { feedList ->
        articleFeeds{
            or{
                feedList.each{ feed ->
                    eq("feed", feed)
                }
            }
        }
    }
}

Grails 笔记 我已经通过添加

orderBy("updated", "desc") 
maxResults("50") 

在域中,并通过尝试

Article.containedInFeeds().listDistinct(max: 50, sort: "updated", order: "desc")

在控制器中,但生成的 SQL 似乎是相同的。

我尝试为更新字段的文章表添加索引,但这并没有提高性能。

article 表大约有 190,000 项。

我还对查询进行了解释(作为我遇到的博客文章的建议),并注意到没有 order by,只有“Using Where”被列为额外内容。使用时按“Using Where、Using Temporary、Using Filesort”的顺序全部列出。

我猜我没有正确创建索引,mysql 实例需要进行一些调整,或两者兼而有之。

编辑

更多信息

我一直在网上搜索有关索引的更多信息,尤其是有关何时使用联接和排序依据的信息。我能找到的最接近的文章是这样的:http://hackmysql.com/case5 但该网站说它不再维护,我猜这些技术已经过时了(它们至少对我不起作用)。我真的很想了解应该如何/为什么要对事物进行索引,而不是简单地给出答案。

这里是请求的表定义和解释输出。

文章列表

CREATE TABLE  `mydb`.`article` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `version` bigint(20) NOT NULL,
    `link` varchar(255) DEFAULT NULL,
    `image_id` bigint(20) DEFAULT NULL,
    `unique_id` varchar(255) DEFAULT NULL,
    `author` varchar(255) DEFAULT NULL,
    `title` varchar(255) DEFAULT NULL,
    `source` varchar(255) DEFAULT NULL,
    `updated` datetime DEFAULT NULL,
    `description` varchar(1000) DEFAULT NULL,
    `date_created` datetime NOT NULL,
    PRIMARY KEY (`id`),
    KEY `FK317B135BE74D38` (`image_id`),
    KEY `Updated_Idx_Test` (`id`,`updated`) USING BTREE,
    CONSTRAINT `FK317B135BE74D38` FOREIGN KEY (`image_id`) REFERENCES `remote_image`  (`id`)
    )
 ENGINE=InnoDB AUTO_INCREMENT=195939 DEFAULT CHARSET=latin1;

_Feed_Articles 表_

CREATE TABLE  `mydb`.`feed_articles` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `version` bigint(20) NOT NULL,
    `article_id` bigint(20) NOT NULL,
    `feed_id` bigint(20) NOT NULL,
    PRIMARY KEY (`id`) USING BTREE,
    KEY `FK9E0121145D70E756` (`article_id`),
    KEY `FK9E012114A51FD776` (`feed_id`),  
    CONSTRAINT `FK9E0121145D70E756` FOREIGN KEY (`article_id`) REFERENCES `article` (`id`),  
    CONSTRAINT `FK9E012114A51FD776` FOREIGN KEY (`feed_id`) REFERENCES `feed` (`id`)
    ) 
ENGINE=InnoDB AUTO_INCREMENT=231684 DEFAULT CHARSET=latin1;

解释输出

我稍微修改了查询,将 where 子句更改为从 10 个 feed_id 中提取:

 feed_articles.feed_id = 1 or
 .
 .
 .
 feed_articles.feed_id = 10

输出:

+----+-------------+-----------+--------+---------------------------------------+--------------------+---------+----------------------------+-------+----------------------------------------------+
| id | select_type | table     | type   | possible_keys                         | key                | key_len | ref                        | rows  | Extra                                        |
+----+-------------+-----------+--------+---------------------------------------+--------------------+---------+----------------------------+-------+----------------------------------------------+
|  1 | SIMPLE      | feed_item | range  | FK9E0121145D70E756,FK9E012114A51FD776 | FK9E012114A51FD776 | 8       | NULL                       | 51909 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | item      | eq_ref | PRIMARY,Updated_Idx_Test              | PRIMARY            | 8       | wiumidev.feed_item.item_id |     1 |                                              |
+----+-------------+-----------+--------+---------------------------------------+--------------------+---------+----------------------------+-------+----------------------------------------------+

感谢到目前为止的帮助。

【问题讨论】:

  • explain 还显示了它使用了哪些索引。您可以粘贴解释和模式的输出,这将有助于回答。
  • 我发布的示例非常简略。今晚我将发布实际的查询/模式,希望能提供一些额外的信息。
  • 另一个更新。我添加了以下键:feed_articles(feed_item, item_id) 现在解释使用索引显示 feed_item 表,但仍然有 using 临时,使用文件排序,这确实是我认为我需要消除的。

标签: mysql hibernate grails


【解决方案1】:

是的,当然。您需要创建索引。 尝试在 feed_articles.feed_id 上创建(第一个)并在 article.id、article.updated(第二个)上创建一个复合索引。

【讨论】:

  • 我已经添加了您建议的索引,但查询仍然想要使用文件排序和临时空间,需要它扫描 60,000 条记录才能返回 50 条。最后,我在查询中添加了“STRAIGHT_JOIN”,它开始使用您建议的索引并摆脱了文件排序和临时空间。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2018-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-19
  • 2021-12-03
  • 1970-01-01
相关资源
最近更新 更多