5

JPAを使用してラージオブジェクトグラフをカスケード保存しようとしています。たとえば(私のオブジェクトグラフは少し大きいですが、十分に近いです):

@Entity
@Table(name="a")
public class A {
  private long id;
  @OneToMany(cascade = CascadeType.ALL, mappedBy = "a")
  private Collection<B> bs;
}

@Entity
@Table(name="b")
public class B {
  private long id;
  @ManyToOne
  private A a;
}

だから私は100以上のBのコレクションを持つAを永続化しようとしています。コードはただです

em.persist(a);

問題は、それが遅いということです。私の節約は約1300msかかります。生成されているSQLを調べましたが、非常に非効率的です。このようなもの:

select a_seq.nextval from dual;
select b_seq.nextval from dual;
select b_seq.nextval from dual;
select b_seq.nextval from dual;
...
insert into a (id) values (1);
insert into b (id, fk) values (1, 1);
insert into b (id, fk) values (2, 1);
insert into b (id, fk) values (3, 1);
...

現在、永続性プロバイダーとしてtoplinkを使用していますが、eclipselinkとhibernateも試しました。バックエンドはoracle11gです。問題は、実際にはSQLをどのように組み合わせるかです。これらの各操作は一括ではなく個別に実行されるため、appserverとdbサーバーの間に5ミリ秒のネットワーク遅延がある場合、200回の個別操作を実行すると1秒追加されます。シーケンスのallocationSizeを増やしてみましたが、それは少ししか役に立ちません。また、直接JDBCをバッチステートメントとして試しました。

for...{
  statement = connection.prepareStatement(sql);
  statement.addBatch();
}
statement.executeBatch();

私のデータモデルの場合、直接JDBCバッチとして実行されるのに約33ミリ秒かかります。Oracle自体は、100以上の挿入に5ミリ秒かかります。

とにかく、休止状態のバルクインサートのようなベンダー固有のものを掘り下げることなく、JPA(私は今1.0で立ち往生しています...)をより速くする方法はありますか?

ありがとう!

4

3 に答える 3

3

INCREMENT BYを汚いものとして増やすのはなぜですか?これは、次のシーケンス値を取得するためのデータベースへの呼び出し回数を減らす最適化であり、INSERTの前にクライアントでID値が割り当てられるデータベースクライアントで使用される一般的なパターンです。これはJPAまたはORMの問題とは見なされません。また、INSERTの前に新しい行ごとに新しいシーケンス番号を取得する必要があるため、JDBCの比較でも同じコストになるはずです。JDBCの場合に別のアプローチがある場合は、EclipseLinkJPAに同じアプローチを実行させることができるはずです。

JPAのコストは、分離されたINSERTシナリオでおそらく最も明白です。これは、トランザクションキャッシュまたは共有キャッシュでの繰り返しの読み取りによるメリットがなく、キャッシュ構成によっては、これらの新しいエンティティをキャッシュ内のキャッシュに配置するための代償を払っているからです。フラッシュ/コミット。

メタデータ処理、クラスの読み込み、場合によってはウィービング、メタモデルの初期化のすべてを行う最初のEntityManagerの作成にもコストがかかることに注意してください。この時間を比較しないようにしてください。実際のアプリケーションでは、これは1回だけ発生し、後続のすべてのEntityManagerは共有メタデータの恩恵を受けます。

これらのエンティティを読み取る必要がある他のシナリオがある場合は、それらをキャッシュに配置するコストによって、それらを取得するコストを削減できます。私の経験では、アプリケーションを一般的な手書きのJDBCソリューションよりもはるかに高速にすることができますが、分離されたテストケースではなく、同時ユーザーのセット全体でバランスが取れています。

これがお役に立てば幸いです。これ以上のガイダンスとEclipseLinkJPA、およびそのパフォーマンスとスケーラビリティのオプションを提供させていただきます。

ダグ

于 2010-07-05T14:54:27.423 に答える
2

解決策は、JDBCバッチ処理を有効にし、EntityManagerを定期的に(バッチサイズと同じ)フラッシュしてクリアすることですが、これを行うベンダー中立の方法を私は知りません。

  • Hibernateでは、hibernate.jdbc.batch_size構成オプションを設定する必要があります。第13章を参照してください。バッチ処理

  • EclipseLinkでは、バッチ書き込みモードがあるように見えます。このスレッドのJeffSutherlandの投稿を参照してください(サイズを指定することも可能です)。

  • このブログ投稿のコメントによると、TopLinkEssentialsではバッチ書き込みは利用できません:(

于 2010-06-24T02:41:34.577 に答える
2

回答してくれたPascalに感謝します。いくつかのテストを行ったところ、パフォーマンスを大幅に向上させることができました。

最適化なしで、約1100msかかるインサートがありました。eclipselinkを使用して、persistence.xmlに追加しました。

   <property name="eclipselink.jdbc.batch-writing" value="JDBC"/>
   <property name="eclipselink.jdbc.batch-writing.size" value="1000"/>

他のプロパティ(Oracle-JDBCなど)を試しましたが、JDBCが最高のパフォーマンス向上をもたらすように見えました。これにより、インサートは約900msになりました。したがって、200ミリ秒のかなり控えめなパフォーマンスの向上。シーケンスallocationSizeを増やすことで、大幅な節約が実現しました。私はこれをするのが好きではありません。JPAに対応するためだけに、シーケンスのINCREMENTBYを増やすのは汚いです。これらを増やすと、インサートごとに約600msの時間が短縮されました。したがって、これらの機能強化により、合計で約500ミリ秒が短縮されました。

これはすべて問題なくダンディですが、それでもJDBCバッチよりも大幅に低速です。JPAは、コーディングを容易にするために支払うにはかなり高い価格です。

于 2010-06-25T16:22:09.450 に答える