我在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
,并且联接的效果现在与“多选”方法。
答案 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
表中。