我在 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)
注释大大提高了性能,将其降低到大约 30 毫秒,从而有效地获得相同的结果。这是生成的带有@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
,并且连接的性能现在与“多选”方法相当。