MySQL& Hibernate:左外连接与多选的性能

时间:2012-04-21 00:09:12

标签: mysql sql hibernate join

我在MySQL中有两个表,“apps”和“icons”,每个表大约有750K行。在Hibernate中,我将它们建模为:

public class App {
    @Basic
    private String title;

    @OneToOne(mappedBy = "app")
    private Icon icon;
    // etc...
}

public class Icon {
    @Basic
    private String name;

    @OneToOne
    private App app;
    // etc...
}

当我添加这种关系时,我很快遇到了性能问题 - 在单个应用程序中读取> 1秒。我检查了Hibernate正在生成的SQL,发现它是这样加入的:

select
    apps.id as app_id,
    apps.title as app_title,
    icons.id as icon_id,
    icons.name as icon_name
from
    apps
left outer join
    icons
        on apps.id=icons.app_id 
where
    apps.id="zyz";

我发现在注释中添加@Fetch(FetchMode.SELECT)可以大大加快性能,将其降低到30ms左右,从而实现相同的效果。这是带有@Fetch(FetchMode.SELECT)注释的生成的SQL:

select
    apps.id as app_id,
    apps.title as title
from
    apps 
where
    apps.id="xyz";


select
    icons.id as icon_id,
    icons.name as icon_name
from
    icons
where
    icons.app_id="xyz";

为什么左外连接这么慢?已加入查询的“解释”显示:

+----+-------------+-------+-------+---------------+---------+---------+-------+--------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows   | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+--------+-------+
|  1 | SIMPLE      | apps  | const | PRIMARY       | PRIMARY | 767     | const |      1 |       | 
|  1 | SIMPLE      | icons | ALL   | NULL          | NULL    | NULL    | NULL  | 783556 |       | 
+----+-------------+-------+-------+---------------+---------+---------+-------+--------+-------+

所以它显然是访问每一行,而不是单行进行多选查询。联接不能使用我对icons.app_id的索引吗?

PS:是的,我在中间时间运行中使用了“RESET QUERY CACHE”。

更新移至bigint主键,用于加入表而不是VARCHAR,并且联接的效果现在与“多选”方法。

1 个答案:

答案 0 :(得分:2)

根据您的解释,我认为问题在于数据库层的架构,而不是应用层。

对图标表的连接没有possible_keys的条目这一事实让我相信你正在运行MyISAM存储引擎或没有FK约束的InnoDB存储引擎。另外,767的密钥长度让我觉得不寻常,我只见过这个值< 10.

  • 如果引擎是MyISAM:向icons.app_id列添加索引。并考虑使用InnoDB引擎,以便您可以建立FK约束,以便最终不会出现孤立的行。

  • 如果引擎是InnoDB:向引用icons.app_id的{​​{1}}添加FK约束。通过添加FK约束,您不仅可以确保数据不会变为孤立,还可以优化表之间的连接,因为您必须在两个列上创建索引。

上述任何一种解决方案都可以大大提高您的性能。让我知道它是怎么回事。

您可以使用以下链接阅读有关某些讨论主题的更多信息:

- 更新 -

以下是一些示例,当您准备添加INT列时,请记住首先在dev上执行此操作,并确保在推送到生产之前解决问题。

对于apps表:

apps.id

对于图标表:

 ALTER TABLE apps
     ADD COLUMN idi int(11) UNSIGNED auto_increment FIRST,
     DROP PRIMARY KEY,
     ADD PRIMARY KEY(idi);

这些改变仅仅是示范性的,但应该足以让你开始朝着正确的方向前进。您可以阅读我发布的有关外键约束的MySQL文档以获取更多信息。现在,通过应用程序和图标之间的FK约束设置,如果删除了任何应用程序,并且还将删除图标匹配 ALTER TABLE icons ADD COLUMN app_idi int(11) UNSIGNED auto_increment AFTER app_id, ADD INDEX (app_idi ), ADD FOREIGN KEY (app_idi) REFERENCES apps(app_idi) ON DELETE CASCADE; 的行,则确保您不会孤立任何数据。如果您不想删除app.id表格中的关联行,则可以将icons更改为ON DELETE CASCADE,它们将与ON DELETE NULL表格取消关联,但仍然驻留在apps表中。