56
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JPAの性能問題と対策

Last updated at Posted at 2016-01-15

Java EEのJPAは便利だけれど、使い方を間違えると性能問題が発生する。
代表的な問題と対策について調べてみました。

JPAを使うメリットとネイティブSQLを使うという選択肢

JPAを使うメリットは次の2つあるかと思います。

  1. 生産性・保守性向上
  2. RDBMSへの依存性排除

1は特に動的にSQLを生成する場合には大きなメリットとなります。
動的にSQLを生成する必要がなく、RDBMSを変更することがなく、かつ、新しい技術への好奇心がないメンバーの場合は、JPAを使わずにネイティブSQLを使った方がよいかもしれません。

代表的な問題と対策

N+1問題

問題

親子関係があるEntityで次のようにデータを取得すると、初めにparentsをとるために1回、ループの中でN回のSQLが発行されてしまう。
(生SQLならjoinで1回なのに)

List<Parent> parents = query(Parent).all();
for (Parent parent: parents) {
  System.out.print(parent.child.name);
}

対策

JPAの場合、JPQLでJOIN FETCHを使用する。

SELECT p FROM Parent p JOIN FETCH p.children

のように記述すると、次のSQLが発行される。

SELECT p FROM Parent p LEFT OUTER JOIN FETCH p.children

他にも、@Fetch@JoinFetchアノテーションを使用する方法もあるが、それぞれ、Hibernate, EclipseLink固有。

バルクinsert, update, delete問題

問題

大量のデータをループでmerge, removeしてしまい、大量のSQLが発行される。

解決策

JPAのbulk処理があるらしいが、これといった定番の方法がみつからない。
いずれにしても、まずは本当に大量データループで処理する必要があるのかを検討する。
ダメな場合は、DB固有のバルク処理にするのが現実的か。

クエリ発行箇所が特定できない問題

問題

DBで性能解析し、問題のあるSQLを特定しても、発行箇所はJPAなので、対応がわかりにくく、どこで発行されたSQLなのかわかりにくい。

解決策

ログに呼び出し元の情報(class, methodなど)とSQLを記録する。

インデックスつけ忘れ問題

問題

SQLやRDBの仕組みを知らなくてもプログラミングできてしまうため、indexの付け忘れがおきやすい。

解決策

レビューや単体性能テストで、インデックス付け忘れを防止する。
そのために、性能要件がシビアな部分は事前に洗い出し、単体性能テストの大量データを準備しておく。

※参考:アノテーションによるインデックス定義

@Entity
// @Tableアノテーションのindexesでインデックスを定義。
// coolumnListはテーブル側の列名でカンマ区切り
@Table(indexes = @Index(name = "member_index", columnList = "NAME,ID", unique = true ))
public class Member implements Serializable {

不要な大容量カラムを無意味に取得してしまう問題

問題

Blobやtextなど大容量のカラムがあり、それらが不要にも関わらず取得してしまい、性能問題になる。

解決策

プロパティの遅延ロードをする(@Basic(fetch = FetchType.LAZY))。
遅延ロードではデータが必要になって初めてSQLを発行する。
このため、トランザクションが終了している場合にはデータを取得できない点に注意が必要。

@Entity
public class ContainsBlob implements Serializable {

    @Id
    private String name;

    @Column
    @Lob
    @Basic(fetch = FetchType.LAZY)
    private byte[] largeData;

    // 省略
}

また、できれば、blobやtextはできるだけ別テーブルにする。
それができなければ、必要なカラムだけを指定したView Tableを用意する。

参考にした情報

56
62
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
56
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?