2

MySQLには「apps」と「icons」の2つのテーブルがあり、それぞれ約75万行あります。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つのアプリでの読み取りに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 |       | 
+----+-------------+-------+-------+---------------+---------+---------+-------+--------+-------+

したがって、複数選択クエリの場合は1行であるのに対し、明らかにすべての行にアクセスしています。結合は、icons.app_idにあるインデックスを使用できませんか?

PS:はい、タイミングの実行の合間に「RESETQUERYCACHE」を使用しました。

更新:主キーに移動し、bigintそれを使用して、の代わりにテーブルを結合しました。結合のVARCHARパフォーマンスは、「複数選択」方式と同等になりました。

4

1 に答える 1

2

あなたの説明によると、問題はアプリケーション層ではなくデータベース層のスキーマにあると思います。

アイコンテーブルへの結合にエントリがないという事実からpossible_keys、MyISAMストレージエンジンまたはFK制約のないInnoDBストレージエンジンを実行していると思います。また、キーの長さが767であると、私は珍しいと思います。この値が10未満であるのは今まで見たことがありません。

  • エンジンがMyISAMの場合:列にインデックスを追加しicons.app_idます。また、InnoDBエンジンの使用を検討してください。そうすれば、FK制約を確立して、行が孤立してしまうことがなくなります。

  • エンジンがInnoDBの場合:をicons.app_id参照するFK制約を追加しますapps.id。FK制約を追加することで、データが孤立しないようにするだけでなく、両方の列にインデックスを作成する必要があるため、テーブル間の結合も最適化されます。

上記のいずれかのソリューションにより、パフォーマンスが大幅に向上します。どうなるか教えてください。

これらのリンクを使用して、いくつかの議論されたトピックに関する詳細情報を読むことができます。

- アップデート -

INT列を追加する準備ができたときに変更する例を次に示します。最初に開発でこれを実行し、本番環境にプッシュする前にこれで問題が解決することを確認してください。

アプリテーブルの場合:

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

アイコンテーブルの場合:

 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;

これらの変更は説明的なものにすぎませんが、正しい方向に進むには十分なはずです。詳細については、外部キー制約について投稿したMySQLドキュメントを参照してください。これで、アプリとアイコンの間にFK制約が設定され、アプリが削除された場合、アイコンが一致app.idする行も削除されるため、データが孤立することはありません。テーブル内の関連する行を削除したくない場合は、に変更iconsできます。これらの行はテーブルからリンク解除されますが、テーブル内に残ります。ON DELETE CASCADEON DELETE NULLappsicons

于 2012-04-21T00:33:27.153 に答える